How To: Working With Timers In Swift

Timers are super handy in Swift, from creating repeating tasks to scheduling work with a delay. This article explains how to create a timer in Swift.

This article shows you how to use the Timer class, formerly known as NSTimer, to schedule timers. We’ll get into repeating and non-repeating timers, using run loops, keeping track of timers, and how you can reduce their energy and power impact.

A timer is created using the Timer.scheduledTimer(...) class method. Its return value is assigned to the constant timer. This constant now contains a reference to the timer, which will come in handy later on.

The parameters of scheduledTimer() are the timer interval of 1 second, which uses a mechanism known as target-action, some userInfo that’s set to nil, and the parameter repeats set to true.

We’ve also coded a function fire(). This is the function that’s called when the timer fires, i.e. roughly every second. By setting target to self and selector to #selector(fire) you’re indicating that whenever the timer fires, the function fire() of self needs to be called.

The above code should run in a class context, for instance in a view controller class. The fire() function is part of the class, and self refers to the current class instance.

In the above code, we’ve added the dictionary ["score": 10] to the timer. When it fire, this dictionary is provided to the fire(timer:) function as its timer parameter. Inside the fire(timer:) function we’re checking if the userInfo property has the type we expect, and we get the right value.

Let’s look at a practical example for using timers, next.

Creating A Countdown Timer

Imagine you’re creating a game. The user has 60 seconds to solve a puzzle and score points. As the count down timer runs out, you’re keeping track of how many seconds are left.

First, we’re creating two properties at the top of our game class. Something like:

var timer:Timer?var timeLeft = 60

The timer doesn’t start immediately. Until it starts, the timer property is nil. At some point the game has started, and we start the timer:

Every time the timer fires it subtracts 1 from timeLeft, and it updates the “time left label”. When the timer reaches zero, timer is invalidated and set to nil.

This will effectively create a countdown timer that counts down from 60 to zero. And at a point in the future, you can of course reset the countdown and start again.

Timers, Runloops and Tolerance

Timers work in conjunction with run loops. Run loops are a fundamental part of threads and concurrency.

It’s easiest to imagine run loops like a ferris wheel. People can enter a passenger car and get moved around by the wheel. In a similar way, you can schedule a task on a run loop. The run loop keeps “looping” and executing tasks. When there are no tasks to execute, the run loop waits or quits.

The way run loops and timers work together, is that the run loop checks if a timer should fire. A timer isn’t a real-time mechanism, because the firing of the timer can coincide with the runloop executing a task. You can compare this to wanting to enter the ferris wheel when there’s no car yet at the bottom entrance. The result is that a timer can fire later than it’s scheduled.

This isn’t necessarily a bad thing. Don’t forget you’re running code on a mobile device that has energy and power constraints! The device can schedule run loops more efficiently to save power.

You can also help the system save power by using a timer property called tolerance. This will tell the scheduling system: “Look, I want this to run every second, but I don’t care if it’s 0.2 seconds too late.” This of course depends on your app. Apple recommends to set the tolerance to at least 10% of the interval time for a repeating timer.

The tolerance property uses the same units as the timeInterval property, so the above code will use a tolerance of 200 milliseconds.

The tolerance will never cause a timer to fire early, only later. And the tolerance will neither cause a timer to “drift”, i.e. when one timer fire is too late, it won’t affect the scheduled time of the next timer fire.

When using the Timer.scheduledTimer(...) class method, the timer is automatically scheduled on the current run loop in the default mode. This is typically the run loop of the main thread. As a result, timers may not fire when the run loop on the main thread is busy, for instance when the user of your app is interacting with the UI.

You can solve this by manually scheduling the timer on a run loop yourself. Here’s how:

The first line of code will create an instance of Timer using the Timer(...) initializer. The second line of code adds the timer to the current run loop using the .commonModes input mode. In short, this tells the run loop that the timer should be checked for all “common” input modes, which effectively lets the timer fire during the interaction with UI.

By the way, a common source for frustration is accidentally using the Timer(...) initializer when you wanted to use the Timer.scheduledTimer(...) to create a timer. If you use the former, the timer won’t fire until you’ve added it to a run loop! And you’ll pull your hair out, because you’re certain you’ve scheduled the timer correctly…

Executing Code With A Delay

A common scenario in practical iOS development is executing some code with a small delay. It’s easiest to use Grand Central Dispatch for that purpose, and not use a Timer.

The following code executes a task on the main thread with a 300 millisecond delay:

Browse Topics

Swift Sandbox

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.