NSTimer
or just Timer
in Swift world is a commonly used way to execute something repeatedly with an interval. For instance, a countdown timer is a perfect example.
A hidden gem is CADisplayLink
- a special flavor of timer that is linked to the device screen refresh rate. Usually, it is 60 frames per second, but in new iPads, it is 120 frames per second.
What is (NS)Timer?
With Timer you can line up one or multiple tasks in the future. You can specify if the timer needs to repeat.
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("1 second passed")
}
In the example above timer will fire every 1 second indefinitely.
To stop the timer you need to invalidate it.
timer.invalidate()
Timers are fired on the main thread. If the app is UI heavy timers won’t fire. To fix that you can: - attach to non-default RunLoop
mode like common mode - it will allow a timer to fire even UI (main thread) is busy
RunLoop.current.add(timer, forMode: .common)
OR - you can synchronize with your screen updates with CADisplayLink
.
What is CADisplayLink?
CADisplayLink
is a special flavor of Timer that lets your apps run a piece of code every time right after the screen has refreshed. Usually, it is 60 frames per second, but in selective devices like new iPads, it is 120 frames per second.
It is a great way to create smooth animations and calculate the next frame. This way you have a perfect timing between the frames unlike the classical Timer
which is not guaranteed to fire on the exact time when the screen is refreshing.
class DisplayLink {
@objc func displayRefreshed(displayLink: CADisplayLink) {
print(displayLink.timestamp)
}
init() {
let displayLink = CADisplayLink(target: self, selector: #selector(displayRefreshed(displayLink:)))
displayLink.add(to: .main, forMode: .default)
}
}
When you create a DisplayLink
it should be linked to an object and specified an objc
function as a selector. This function will be fired as soon as redraw on the screen happens. We can see it by printing out the timestamp.
...
93095.013681187
93095.11224511401
93095.12891178101
93095.145578448
93095.162245115
93095.178911782
93095.19557844901
93095.21224511601
...
CADisplayLink
has a property preferredFramesPerSecond
that lets you specify a callback rate calculated in frames per second. It means you can specify your refresh rate when you want the timer to fire. It is a great way if you don’t need to execute your code every time screen refreshes.
Timer vs CADisplayLink
Timer
and CADisplayLink
both are ways to fire piece of code with an interval. These two approaches are different.
Timer
is useful when you just want to execute something let’s say every 5 seconds without it being tied to screen redrawing.
CADisplayLink
is tied to screen refresh rate which is either 60 or 120 frames per second. It is fired right after screen redraws and you have maximum amount of time to execute the code before the next screen refresh. This approach shines if you need to create seamless animations or it can be useful in game development where you can’t avoid any screen refresh drops.
TL;DR
We all might know NSTimer
(or just Timer
) which is good for executing piece of code after an interval with or without repeating it.
In some cases it’s worth to consider CADisplayLink
especially if you want to work with animations and get most of the time between screen redraws itself.