Continue countdown timer when app is in background / paused

I have a functional countdown timer.

The problem is, I need to continue the countdown when the user puts the app in the background. Or pauses the application? I'm not sure if the correct terminology is to describe this action.

In other words, the user opens the app and starts the countdown. The user should be able to use other apps, but the countdown still works. Additionally, the countdown should be able to run forever until it completes or when the user terminates the application.

Here is the code for my timer:

    class Timer{
var timer = NSTimer();
// the callback to be invoked everytime the timer 'ticks'
var handler: (Int) -> ();
//the total duration in seconds for which the timer should run to be set by the caller
let duration: Int;
//the amount of time in seconds elapsed so far
var elapsedTime: Int = 0;
var targetController = WaitingRoomController.self

var completionCallback: (() -> Void)!

:param: an integer duration specifying the total time in seconds for which the timer should run repeatedly
:param: handler is reference to a function that takes an Integer argument representing the elapsed time allowing the implementor to process elapsed time and returns void
init(duration: Int , handler : (Int) -> ()){
    self.duration = duration;
    self.handler = handler;

Schedule the Timer to run every 1 second and invoke a callback method specified by 'selector' in repeating mode
func start(){
    self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "onTick", userInfo: nil, repeats: true);

invalidate the timer
func stop(){
    println("timer was invaidated from stop()")

Called everytime the timer 'ticks'. Keep track of the total time elapsed and trigger the handler to notify the implementors of the current 'tick'. If the amount of time elapsed is the same as the total duration for the timer was intended to run, stop the timer.

@objc func onTick() {
    //increment the elapsed time by 1 second
    //Notify the implementors of the updated value of elapsed time
    //If the amount of elapsed time in seconds is same as the total time in seconds for which this timer was intended to run, stop the timer
    if self.elapsedTime == self.duration {

    println("timer was invalidated from deinit()")




source to share

4 answers

I suggest canceling the timer and saving NSDate

when the app goes into the background. You can use this notification to detect when an application is going into the background:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseApp", name: UIApplicationDidEnterBackgroundNotification, object: nil)


Then cancel the timer and save the date:

func pauseApp(){
    self.stop() //invalidate timer
    self.currentBackgroundDate = NSDate()


Use this notification to detect the returned user:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "startApp", name: UIApplicationDidBecomeActiveNotification, object: nil)


Then calculate the difference from the stored date to the current date, update your counter, and start the timer again:

func startApp(){
    let difference = self.currentBackgroundDate.timeIntervalSinceDate(NSDate())
    self.handler(difference) //update difference
    self.start() //start timer




You shouldn't be doing this. The timer runs the processor at full capacity, which is bad.

Only certain types of apps are allowed to actually run code indefinitely from the background, for example. VOIP applications and music applications.

Several variants:

  • Set up a local notification for a future date (which will send a message to your app or restart it if it no longer works.)

  • When you start your timer, write down the current NSDate. Then, when your app returns to the foreground or restarts, choose to compare the current date with the saved date, specify the time elapsed since that point, and decide if your timer is still over.



Try this instead. This code updates the time when the app returns from background / suspend and in an active state.

class ProgressV: UIView {

var timer: NSTimer!
var expirationDate = NSDate()

//Set the time that you want to decrease..
var numSeconds: NSTimeInterval = 36000.0 // Ex:10:00 hrs

func startTimer()
    // then set time interval to expirationDate…
    expirationDate = NSDate(timeIntervalSinceNow: numSeconds)
    timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(updateUI(_:)), userInfo: nil, repeats: true)
    NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

func updateUI(timer: NSTimer)
  // Call the currentTimeString method which can decrease the time..
   let timeString = currentTimeString()
   labelName.text = timeString
func currentTimeString() -> String {
    let unitFlags: NSCalendarUnit = [.Hour, .Minute, .Second]
    let countdown: NSDateComponents = NSCalendar.currentCalendar().components(unitFlags, fromDate: NSDate(), toDate: expirationDate, options: [])

    var timeRemaining: String
    if countdown.hour > 0
        timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
    else {
        timeRemaining = String(format: "%02d:%02d:%02d", countdown.hour, countdown.minute, countdown.second)
    return timeRemaining





Make sure to enable background and set the required values ​​in xCode. Strange, but even if background is disabled, this code works. This seems to work any timers after setting application.beginBackgroundTask {}

in the AppDelegate.

I am using this code:

In AppDelegate add below code:

func applicationDidEnterBackground(_ application: UIApplication) {

    application.beginBackgroundTask {} // allows to run background tasks


And call the method below wherever you want.

func registerBackgroundTask() { .background).asyncAfter(deadline: + 5, qos: .background) {




All Articles