Search code examples
iosswiftmultithreadingtimernstimer

Timer is being fired twice and not nilled


I'm following the suggestions given in this answer. Still my timer is sometimes not nil

func recreateTimer(){

        let timerLog = OSLog(subsystem: "LocationLogger", category: "Timer")

        guard shouldUseTimer == true else {
            os_log("we are using CoreMotion, no need to set timer", log: timerLog, type: .error) 
            return
        }
        if dwellingTimer != nil{

            dwellingTimer?.invalidate()
            dwellingTimer = nil
        }

        lastDwelling = lastLocation

        os_log("Moved more than 160 | invalidated previous timer began new timer", log: timerLog, type: .error)

        DispatchQueue.main.async { [weak self] in

            guard self?.dwellingTimer == nil else{
                os_log("Timer was not niled!!!", log: timerLog, type: .error)
                return
            }
            self?.dwellingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: false, block: { timer in
                os_log("we reached 1 minute of no movement",log: timerLog, type: .fault)
                self?.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
                self?.beginMotionTracking()
                os_log("Timer is deallocated & invalidated | Accuracy: %{public}f | RemainingTime: %{public}f",log: timerLog, type: .fault,(self?.locationManager.desiredAccuracy)!, UIApplication.shared.remainingTime())
            })
        }
        os_log("New Timer is 'likely' set now", log: timerLog, type: .error)
    }

Sometimes I get hits on

os_log("we are using CoreMotion, no need to set timer", log: timerLog, type: .error).

Solution

  • Thanks to solenoid and Paulw11 comments, I learned that all executions related to a timer should be done in a serial manner.

    Also see invalidate

    You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

    So in addition to invalidating and nilling (you don't want to give non-nil for an object that no longer conveys any meaning), make sure that you:

    1. use the same thread
    2. the thread you are using is serial
    3. depending on where you make changes onto the timer you may or may not need synchronization.