Search code examples
iosxcodeswiftbackgroundnstimer

Keep running NSTimer switching Background and Foreground


I am trying to create a NSTimer which is started manually by the user in foreground, and as expected works perfectly:

timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateCounter"), userInfo: nil, repeats: true)

where updateCounter() simply updates a UILabel in the UI. Now what I want to achieve is to get it running also when the user leaves the application. I googled something and found this code (which I translated in Swift):

func applicationDidEnterBackground(application: UIApplication) {
            var bgTask = UIBackgroundTaskIdentifier()
            bgTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler { () -> Void in
                UIApplication.sharedApplication().endBackgroundTask(bgTask)
            }
            // Start the long-running task and return immediately.
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in



               var vc = TimerViewController()
               // Fire some methods
               UIApplication.sharedApplication().endBackgroundTask(bgTask)
               bgTask = UIBackgroundTaskInvalid
               });
        }

Basically from what I can understand here is that when app reaches background I can fire some methods from a new instance of my TimerViewController(), which is supposed to handle the NSTimer for the background phase.

Now does this imply that I need to use a method from this vc, or another, which starts another NSTimer starting from the exact time in which the foreground-one was stopped (because app entered background)?

If yes, to resume my first NSTimer when the user opens the app again (meaning foreground again :D) how should I behave? Stop the second NSTimer and resume the first with updated counter?

Actually even a sketch of the solution is appreciated, I'm just trying to figure out how to handle the situation.

further question: If what it's written above turn out to be correct how can I manage the instances of the viewcontroller to get the timer_value(token for something like secondsLeft) when a switch from background to foreground or vice-versa happens?


Solution

  • Instead of trying to maintain an NSTimer in the background (which is impossible beyond about 10 minutes), what you want to do is record when your timer started and save that in NSUserDefaults. You can still update your timer every second, but just recalculate the label based on the start time (remember, NSTimer doesn't promise that it will run on time; it could be late for many reasons and you'll drift).

    Now there is no reason to run in the background. When your view controller comes onscreen (viewDidAppear:), just update the label again, based on the start time saved in NSUserDefaults.

    There is no way in iOS to run indefinitely in the background. That's by design. But there's also no reason for you to be wasting system resources trying to update your UI when you're not onscreen. Just update it when you are.