Search code examples
swiftmacostimernstimer

Swift Timer lags


I know Timer is not guaranteed to run at exactly the rate you asked for. However, I have just started an all new project with this:

class ViewController: NSViewController {

    var timer = Timer()
    var count: Double = 0.0
    var timeStamp: Date = Date()

override func viewDidLoad() {
    super.viewDidLoad()
    self.timer = Timer.scheduledTimer(timeInterval: 0.1,
                                      target: self,
                                      selector: #selector(self.updateCounting),
                                      userInfo: nil,
                                      repeats: true)
}

@objc func updateCounting(){
    let duration = self.timeStamp.distance(to: Date())
    print("Counting \(self.count) vs. Real life \(duration)")
    self.count = self.count + 0.1
}

}

I am expecting to get roughly the same results between count and duration but I see strange things like this:

Counting 60.50000000000059 vs. Real life 60.64057409763336
Counting 60.60000000000059 vs. Real life 60.741435050964355
Counting 60.70000000000059 vs. Real life 70.84065008163452
Counting 60.800000000000594 vs. Real life 80.94094407558441
Counting 60.900000000000595 vs. Real life 82.99448704719543

Tested on Xcode 11.5, on iMac and MacBook Pro. Am I doing something wrong here?


Solution

  • Not wrong, but you missed the Energy Efficiency Guide for Mac Apps:

    Your app was just napping.

    Check the ProcessInfo & search for Managing Activities. An example (bad one) is to change your AppDelegate.swift to:

    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
        private var activity: NSObjectProtocol!
    
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            let options: ProcessInfo.ActivityOptions = [.userInitiatedAllowingIdleSystemSleep]
            let reason = "No napping!"
            self.activity = ProcessInfo.processInfo.beginActivity(options: options, reason: reason)
        }
    
        func applicationWillTerminate(_ aNotification: Notification) {
            ProcessInfo.processInfo.endActivity(activity)
        }
    }
    

    But don't do this, it's bad for users, MacBook Pro battery life, etc.