Search code examples
swifttimer

background run timer swift


I want the timer to run even when I close the application. I want it to work in the background counter. the timer goes back one second when I run it.(counter) How can I do that?

class TimerViewController: UIViewController {
    var selectedDay: String?

    var seconds = 
    var timer = Timer()

    @IBAction func start(_ sender: AnyObject) {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(TimerViewController.counter), userInfo: nil, repeats: true)

        sliderOutlet.isHidden = true
        startOutlet.isHidden = true
    }

    @objc func counter() {

        seconds -= 1
        favoriteDayTextField.text = String(seconds) + " Seconds"
        var bgTask = UIBackgroundTaskIdentifier(rawValue: seconds)
        bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
            UIApplication.shared.endBackgroundTask(bgTask)
        })

        if (seconds == 0) {
            timer.invalidate()

            if self.button.isOn {
               updateState()
            } else {
               updateState1()
            }
       }
    }
}

Solution

  • I am not clear what you want to achieve. Suppose you want to update the label after the timer has started each 1 second. Then one approach will be:-

    1. Start the timer in view did load if the duration is remaining.
    2. Register for applicationWillTerminate
    3. In application will terminate save the passed duration and terminated time to calculate remaining time in next launch.

      var remainingDuration: TimeInterval!
      
      override func viewDidLoad() {
       super.viewDidLoad()
      
       let remainingDurationFromLastLaunch = UserDefaults.standard.object(forKey: "duration") as? TimeInterval ?? 0
       let lastTerminatedTime = UserDefaults.standard.object(forKey: "lastTerminatedDate") as? Date ?? Date()
      
      if Date().timeInterval(since: lastTerminatedTime) > remainingDurationFromLastLaunch {
      
        remainingDuration = remainingDurationFromLastLaunch - Date().timeInterval(since: lastTerminatedTime)
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(TimerViewController.counter), userInfo: nil, repeats: true)
        NotificationCenter.default.addObserver(self, selector: #selector(TimerViewController.applicationWillTerminate), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
      
      } else { //Duration is passed....Do whatever you want
      
       }
      }
      
      @objc func counter() {
      
      remainingDuration -= 1
      if remainingDuration == 0 { //Duration is passed....Do whatever you want
         timer.invalidate()
         timer = nil
       } else {
         favoriteDayTextField.text = String(remainingDuration) + " Seconds"
      }
      }
      
      @objc func applicationWillTerminate() {
         if timer != nil {
         backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
          UserDefaults.standard.set(remainingDuration, forKey: "duration")
           UserDefaults.standard.set(Date(), forKey: "lastTerminatedDate")
      
          }
          self?.endBackgroundTask()
         }
        }
      
      func endBackgroundTask() {
      UIApplication.shared.endBackgroundTask(backgroundTask)
      backgroundTask = UIBackgroundTaskInvalid
      }