iOS Timer Tutorial
In this iOS Timer tutorial, you’ll learn how timers work, affect UI responsiveness and battery and how to work with animations using CADisplayLink. By Fabrizio Brancati.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
iOS Timer Tutorial
20 mins
- Getting Started
- Creating Your First Timer
- Adding Timer Tolerance
- Trying Out Timers in the Background
- Understanding Run Loops
- Utilizing Run Loop Modes
- Adding a Task Completion Animation
- Showing the Animation
- Stopping a Timer
- Using CADisplayLink for Smoother Animations
- CADisplayLink to the Rescue!
- Where to Go From Here?
Imagine that you’re working on an app where you need a certain action to be triggered in the future – maybe even repeatedly. It would be helpful to have a concept in Swift that offers this functionality, right? That’s exactly where Swift’s Timer
class comes into the picture.
It is common to use Timer
to schedule things in your app. This could be, for example, a one-time event or something that happens periodically.
In this tutorial, you’ll learn exactly how Timer
works in iOS, how it affects the UI responsiveness, how to improve device power usage working with Timer and how to use CADisplayLink
for animations.
This tutorial walks you through building a ToDo app. The ToDo app tracks the progression of time for a task until a task is completed. Once the task is completed, the app congratulates the user with a simple animation.
Ready to explore the magic world of timers in iOS? Time to dive in!
Getting Started
Download the project materials using the Download Materials button at the top or bottom of this tutorial.
Open the starter project and check out the project files. Build and run. You’ll see a simple ToDo app:
First, you’ll create a new task in your app. Tap the + tab bar button to add a new task. Enter a name for your task (e.g., “Buy Groceries”). Tap OK. Great!
Added tasks will reflect a time signature. The new task you created is labeled zero seconds. You’ll note that the seconds label does not increment at the moment.
In addition to adding tasks, you can mark them as complete. Tap on the task you created. Doing so crosses out a task name and labels the task as completed.
Creating Your First Timer
For your first task, you’ll create the app’s main timer. As mentioned above, Swift’s Timer
class, otherwise known as NSTimer
, is a flexible way to schedule work to happen at some point in the future, either periodically or just once.
Open TaskListViewController.swift and add the following variable to TaskListViewController
:
var timer: Timer?
Then, declare an extension at the bottom of TaskListViewController.swift:
// MARK: - Timer
extension TaskListViewController {
}
And add the following code to the TaskListViewController
extension:
@objc func updateTimer() {
// 1
guard let visibleRowsIndexPaths = tableView.indexPathsForVisibleRows else {
return
}
for indexPath in visibleRowsIndexPaths {
// 2
if let cell = tableView.cellForRow(at: indexPath) as? TaskTableViewCell {
cell.updateTime()
}
}
}
This method:
- Checks if there is any visible row in
tableView
containing tasks. - Calls
updateTime
for every visible cell. This method updates the cell time. Have a look at what it does in the TaskTableViewCell.swift file.
Now, add the following code to the TaskListViewController
extension:
func createTimer() {
// 1
if timer == nil {
// 2
timer = Timer.scheduledTimer(timeInterval: 1.0,
target: self,
selector: #selector(updateTimer),
userInfo: nil,
repeats: true)
}
}
Here, you:
- Check if
timer
contains an instance of aTimer
. - If not, set
timer
to a repeatingTimer
that callsupdateTimer()
every second.
Next, you need to create a timer whenever a user adds a task. To do this, call the following method as the first line of code in presentAlertController(_:)
:
createTimer()
Build and run your app.
To test your work, create a couple of new tasks using the same steps as before.
You’ll note that the table view cell’s time label now updates the elapsed time every second.
Adding Timer Tolerance
Increasing the number of timers in your app increases the risk for less app responsiveness and more power usage. Every Timer
tries to fire itself at a precise one-second mark each second. This is because a Timer
‘s default tolerance value is zero.
Adding tolerance to a Timer
is an easy way to reduce the energy impact it has on your app. It lets the system fire the timer any time between the scheduled fire date and the scheduled fire date
For repeating timers, the system calculates the next fire date from the original fire date, ignoring the tolerance applied at individual fire times. This is to avoid time drift.
To avoid any side effects of decreased app responsiveness and increased power usage in your app, you can set the timer
property’s tolerance
to 0.1.
In createTimer()
, right after setting the timer
to a Timer
, add this line of code:
timer?.tolerance = 0.1
Build and run.
The changes may not be visually obvious. However, your users will benefit from the app responsiveness and power efficiency.
Trying Out Timers in the Background
You may wonder what happens to a timer when your app goes to the background.
To investigate this, add the following code as the first line of updateTimer()
:
if let fireDateDescription = timer?.fireDate.description {
print(fireDateDescription)
}
This lets you see when a Timer
fires from the Console.
Build and run. Next, add a task as you have before. Return to your device home screen, then open the ToDo app again.
You will see something similar to this in the Console:
As you can see, when your app enters the background, iOS pauses any running timers. And when the app enters the foreground again, iOS resumes the timers.
Understanding Run Loops
A run loop is an event-processing loop that schedules work and manages the receiving of incoming event. A run loop keeps a thread busy when there is work, and it puts a thread to sleep when there is an absence of work.
Every time you launch your app on iOS, the system creates a Thread
— the main thread. Each Thread
has a RunLoop
automatically created for it as needed.
But why is this relevant for you? Currently, each Timer
fires on the main thread and is attached to a RunLoop
. As you may know, the main thread is in charge of drawing the user interface, listening for touches and such. When the main thread is busy with other things your app’s UI may become unresponsive and behave unexpectedly.
Did you notice that the task cell’s elapsed time label pauses when you drag the table view? There is a lag in which the timer does not fire when you scroll the table view.
A solution to this problem is to set the RunLoop
to run timers with a different mode
. More on this next!