Timer im background Task not possible?

Hey there,


I have an App which should do something in background.


So I wrote some code in AppDelegate:


func applicationDidEnterBackground(_ application: UIApplication) {
        print("BackgroundActivity in Delagate")
            Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { timer in
                //Do something
                } else {
                    //Do something different
                }
            }
    }


Now, every time if the application enter background I get this error in console:

Can't end BackgroundTask: no background task exists with identifier 1 (0x1), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.


I tried to fix it with using

DispatchQueue.global(qos: .background).async {
            self.BackgroundActivity()
            DispatchQueue.main.async {
                print("Hello")
            }
        }

in viewDidLoad() of the initial view controller to start the background task already if the application is in foreground. The tast does not work and when I go to background I get the same error.


Anyone an idea what to do?


Thanks a lot!

Replies

Timers are not made to work in background, except some specific case. See:

https://stackoverflow.com/questions/42319172/swift-3-how-to-make-timer-work-in-background

and

h ttps://www.raywenderlich.com/5817-background-modes-tutorial-getting-started

As Claude31 says, running timers in the background is tricky because, in general, the system will suspend your app shortly after it moves to the background, and once the app is suspended your timers won’t fire. Which brings us to this:

I have an App which should do something in background.

Can you explain more about what you’re trying to do here? Some things are perfectly feasible (for example, using the background task API to get extra time to log out of a server) but others are not (for example, running a timer every 5 minutes while your app is in the background). If you can explain more about your high-level goal, we should be able to point you in the right direction.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

ps DTS is closed 21 Dec through 1 Jan.

Thanks for the answers.


If the App is in background it should wait for 30 minutes and send a push notification

(don't ask why, but that's the intention).

If the App is in background it should wait for 30 minutes and send a push notification

A push notification? Or a local notification? If it’s a local notification, you can schedule that to be delivered at a specific time. If it’s a push notification, where your app needs to reach out over the network to tell your push provider in generate the notification, you’re probably out of luck. There’s no general-purpose way for an iOS app to resume itself in the background at an arbitrary time.

(don't ask why, but that's the intention).

To be clear, the more background info you can give me, the more likely it is that I’ll be able to help.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Sry for the late reply.


Yes it's actually a local notification. There is no web service needed.


The only thing the application wants to do in background, is to wait for 30 minutes and then send a notification.

If you tap on the notification the user should be navigated into the app and all other steps are inclued there.

The only thing the application wants to do in background, is to wait for 30 minutes and then send a notification.

That’s possible, but it doesn’t work the way you describe it. Rather, you schedule a local notification for a specific time and then your app suspends. The system will present the notification on your behalf at the scheduled time. If the user taps the notification, it’ll resume (or relaunch) your app and tell it about that.

Check out the UserNotifications, and specifically the

UNCalendarNotificationTrigger
and
UNTimeIntervalNotificationTrigger
triggers.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you. So do I need both notification triggers or is one enough? If there is a choice I would prefer the interval notification trigger. (Listening also shows what I want). Do I need to create a new class for this or can I use a specific function? Remember the trigger should starts if application enters background... Thanks again!

So do I need both notification triggers or is one enough?

Just one is fine. I mentioned both because the correct one to use depends on your specific problem.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

My current code is the following:

//Asking for permissions
        let center = UNUserNotificationCenter.current()
        print("Authorizsation:")
        center.requestAuthorization(options: [.alert, .sound]) { granted, error in
            print("Error")
        }
        
        //create the notification
        let content = UNMutableNotificationContent()
        content.title = "Hello"
        content.body = "30 Sec are over"
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (30), repeats: true)
        
        // Create the request
        let uuidString = UUID().uuidString
        let request = UNNotificationRequest(identifier: uuidString,
                    content: content, trigger: trigger)

        // Schedule the request with the system.
        //let notificationCenter = UNUserNotificationCenter.current()
        center.add(request) { (error) in
           if error != nil {
              print("Error2: \(error)")
           }
        }


Bad thing is that nothing happens...

In console I can see:

Authorizsation:

Error


For testing I implement it in an action of a button. In future it should starts automatically with viewDidLoad() or applicationDidEnterBackground() or (much better) a system function after the iPhone is booting.


Do you have any further ideas?

First thing first, you need work out why you’re not authorised. If you step on line 5, what does

error
look like?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Error is "nil". (Even though I allowed notifications first time)


Additionally I get this error in the console as soon as my app is running:

invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.


If I quit the app with swipe from bottom to top (iPhone 11 Simulator) I get the error code like I get with the timer. So it seems like the trigger is not working correctly:

Can't end BackgroundTask: no background task exists with identifier 1 (0x1), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.



I tried the same code in a macOS App and there it works... very strange...

error
is
nil

In which case, granted is

true
, right?

Additionally I get this error in the console as soon as my app is running:

Someone is attempting to run the run loop in the common modes. This is nonsense. You can schedule a run loop source in the common modes, but you always have to run a run loop in a specific mode.

You should do what the message says, that is, set a symbolic breakpoint on

_CFRunLoopError_RunCalledWithInvalidMode
and then look at the backtrace to see who is doing this.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for the advice!

Now I find the issue: the notification is not coming if the app is open (in foreground) when it should come.

So if I cklick on home button after activate the notification it works. But unfortunately having the app in foreground stops the notification for ever. So it is not delivered it I quit the app...

Now I find the issue: the notification is not coming if the app is open (in foreground) when it should come.

See

userNotificationCenter(_:willPresent:withCompletionHandler:)
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hello, I have the same problem. I'm creating an app for Interval Training (HIIT) and I need a Timer to continue running even if the user exists the app to go to another app. I've seen many apps doing exactly this ("IntervalTimer" or "HIIT & Tabata") but I can't find the way to do it. Do you have any suggestion?

Post not yet marked as solved Up vote reply of hmap Down vote reply of hmap