If a timer is fired on the main thread's run loop, you will probably have a problem with either the timer function or UI operation.
For example, suppose you have triggered a timer. There is also a tableView in your app that shows a list to the user and the user is scrolling the table. Meanwhile, the timer's time elapses. We expect that the code block of the timer executes immediately, but it doesn't until the user ends the scroll operation. In many cases, this isn't what we want.
The source of this problem and the problem you mentioned in your question is the fact that you have run the timer on the main thread, where your task and all UI operations are handled serially. serial means one task is executed only if the previous task finished.
To solve that, one solution is to call timers on a background thread's run loop like below:
DispatchQueue.global(qos: .background).async {
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
print("After 3 seconds in the background")
}
RunLoop.current.run()
}
The scheduledTimer method creates a timer and schedules it on the current run loop. The following code has the same meaning:
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: 6, repeats: false) { _ in
print("After 6 seconds in the background")
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
Notice: if your timer is called repeatedly, don't forget to call invalidate() of the timer at the end of the task, otherwise the run loop will keep a strong reference to the target object of the timer that could result in memory leak.