I have such code to call Timer scheduled method. As far I've never had a problem with timer methods being called. I've even didn't used RunLoop.
private func expireToken(afterTimeInterval timeInterval: TimeInterval) {
print("[DEBUG] Schedule expiration, time: \(timeInterval), main thread \(Thread.isMainThread)")
let timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { timer in
print("[DEBUG] Expiring token")
self.tokenContainer = .expired
timer.invalidate()
}
RunLoop.current.add(timer, forMode: .common)
}
This method seems to be called from background thread as first print statement logs to console such information
[DEBUG] Schedule expiration, time: 10.0, main thread false
A Timer can only be scheduled on a RunLoop that is processed (regularly call one of the
run...
methods). Most threads do not process a RunLoop.Generally the right answer for something like what you've described is to run the timer on the main runloop. Timers themselves are extremely cheap.
With Swift concurrency, you can also easily do this with a Task and
Task.sleep
. This will let you.cancel()
the task exactly as you.invalidate()
a Timer. I would recommend this approach in new code. Something like:You can also use
DispatchQueue.asyncAfter
to run something after a given time without a Timer.And finally, if you do need to process your own background RunLoop, see Run Loops in the Threading Programming Guide. This is rarely needed, however.
As an unrelated note, there is no need to invalidate a timer that has already fired and does not repeat. It invalidates itself: