Using the Swift 4.2 Timer class in Xcode 10

Developers often need to do something many times and at regular intervals. One common application of such a use case is checking some process’s or device’s status, a technique called “polling.” In this tutorial, I’ll show you how to use the Swift 4.2 Timer class in Xcode 10 to repeatedly perform a simple task, every second, for 1 minute (60 seconds) — and update the user interface (UI) on each “tick.” You’ll learn how to start a timer ticking, pause it, resume it, stop it, and do something on each tick. Here’s what we’ll create during this tutorial — and the source code is available for download from GitHub:

To emphasize that the UI remains responsive when a timer is used judiciously, I added a UISlider to my storyboard and tested my code. Here’s the result:


If you want to read some background and history on timers, keep going from this point. If you want to jump right into coding timers, click here.

Timers are often used for the following purposes, which is why I mentioned polling:

  • checking for new data;
  • checking for changes made to data;
  • checking for an existing device that woke up;
  • checking for a new device that was connected to a Mac or iPhone; or,
  • find out if a communications peer or server is still there, has crashed, has become disconnected, or has shut down.
CODING TIP: Generally, you want to use a timer for short-lived light-weight tasks since, unless you jump through some hoops, those tasks will run on the main thread. If you ran a task that takes 1 second to complete on a timer that fires every second, your user interface would be unresponsive! Used correctly, timers can allow you to write semantically clear code without getting into asynchronous/background (“threaded”) code (using something like Grand Central Dispatch (GCD)).

CODING TIP: Conversely, you can use timers to control or “pace” the spinning off of heavy-duty, long running tasks. For example, you may have a realtime application that must constantly and regularly poll for updated data, then recalculate/refresh pivot table(s) that summarize statistically meaningful metrics like averages, sums, standard deviations, etc., and finally update your UI with the new values. On each tick, you could start each update cycle using GCD.

Polling

Polling is a proven, predictable, and simple method for a client to regularly check for some changing condition(s) or state(s) on a server. Or, an app may need to poll a data source for updates. There are many other examples I could mention. Using the Timer class for such purposes is often the best option to use in code. They’re semantically clear, easy to set up, and time-savers when development pressures are almost always high.

Yes, there are alternative solutions to polling like interrupts. There seems to be this never-ending debate over which technique is superior. These type of debates have merit in the sense that people definitely should always be thinking about improving software systems. On the other hand, such debates tend to bore me to near death as there rarely ever is a “perfect” solution and I’m a diehard pragmatist: I want techniques that work and are simple to implement.

Oftentimes, there is no alternative to polling. Suppose there’s some free public data source, like weather information, accessible through a REST API, where the data is encoded in JSON. Say the data owners require clients to sign up for an API key. Most such free services I’ve used just update data fields at the source — and that’s it. They don’t send push notifications to every developer with an API key. They expect clients to poll their API to find out if, for example, the temperature has changed. While some such services allow retail clients to subscribe to push notifications to get informed about changes, many don’t support this type of overhead for clients who are developers.

There are other use cases beyond polling, like coordinating an app’s performance of a series of steps at regular intervals.

Encoding a basic Timer in Swift

I’ll walk you through setting up and configuring a Timer in Swift, starting it ticking, pausing it, resuming it, stopping it, and restarting it. You can download my sample project from GitHub, which is finished and ready to run, and read along, or follow the steps I describe below and build the project from scratch. Either way, read along with me.

Create a new Xcode project using the iOS Single View App template.

Storyboard

Add two UIButton instances, a UITextField instance, and a UIProgressView in Main.storyboard. Add Auto Layout constraints. Create an IBAction for each button and an IBOutlet for the text field and progress view by control-dragging from the storyboard into ViewController.swift, like this:

Open up the ViewController.swift file. Add the following properties to the ViewController class:

Remember that since we rarely initialize an instance of UIViewController subclass programmatically, all those properties I just added were either optionals or had initial values assigned.

To keep my code “bounded,” I generally always set the maximum number of times that a timer can be fired (totalTicks). Even in cases where my timers run for the whole life of an app, I generally enforce this boundary condition to help during development — and especially debugging — of timer code.

Initialization

When we start the app, we’ll do some simple initialization:

Creating and starting the timer

To start a timer, we literally need to create one. In use cases like I laid out in the introduction to this tutorial we use the Timer class’s scheduledTimer type method. Please review the method’s documentation. It says that the scheduledTimer method “Creates a timer and schedules it on the current run loop in the default mode.” I want you to learn about the Timer class’s lifecycle, so we’ll create our Timer and destroy Timer instances to allow starting, pausing, and resuming. Add the following code to the “Start” button’s IBAction:

The first line in this function creates a Timer instance I’ve declared as countingTimer. Let’s review the scheduledTimer method’s arguments:

  • timeInterval: The time between “firings of the timer” (kinda like the time between ticks on a clock, but less than or equal to or more than a second);
  • target: The object on which we call the selector, and since we define the selector in ViewController, that’s this object, ViewController, or as we write it as an argument, self;
  • selector: A function that is called on each timer fire or “tick” as I like to call them;
  • userInfo: Almost any extra info that we want to associate with our timer — and send to the selector;
  • repeats: set this to true to make your timer repeatedly call the selector, once per timeInterval — we’ll discuss false in the conclusion.

Wait a minute. Notice that the line calling scheduledTimer now has the error (!) Use of unresolved identifier ‘onTimerTick’. That’s because the selector argument of scheduledTimer points to a function named onTimerTick that gets called on every timeInterval as specified by my tickRate constant — but onTimerTick hasn’t been defined yet! Let’s do that next, and note that we’ll discuss tolerance and RunLoop in the conclusion.


What to do on each tick

You can name your timer fire handler function whatever you want, but Apple advises you use the following declaration:

Hey Apple, that’s Objective-C and I’ve got the “Language” setting on the “Documentation” site set to “Swift.” Oops! Oh, well… Y’all know how to translate that to Swift, like I did here:

What? Another error? This time it’s (!)Argument of ‘#selector’ refers to instance method ‘onTimerTick(timer:)’ that is not exposed to Objective-C. Why?

While it seems like Swift has its own native Timer, it’s really calling Objective-C. How do I know? I looked at Apple’s documentation on Timer. Here’s the class’s declaration:

Every time our (Objective-C) timer fires, it calls our onTimerTick Swift function, and therefore (see bullet “SE-0160”):

…the compiler will emit warnings about places where the Objective-C entry points for these inference cases are used, e.g., in a #selector or #keyPath expression, via messaging through AnyObject, or direct uses in Objective-C code within a mixed project. The warnings can be silenced by adding an explicit @objc.

So I let the Xcode error helper fix the problem:

Pausing/stopping the timer

When you want to start or “resume” a Timer, you must create and configure one as I showed you above. When you want to stop or “pause” a Timer, you must destroy (invalidate()) it as I showed you above and will show you in the next code snippet.

If you read the documentation on Timer, you should’ve noted that:

Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A nonrepeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate() method. … Invalidating the timer immediately disables it so that it no longer affects the run loop. … Once invalidated, timer objects cannot be reused.

Let’s finish up by adding code to be called when our “Stop” button is pressed, allowing us to pause or stop our timer:

Hey. That was pretty simple — overall. There were some error messages we had to deal with. And there were a couple of important sounding properties we set, but which I haven’t yet explained. Before concluding by defining these timer details, let’s look at an example of a timer.

Applications of timers

Let me show you how I have used timers to simplify the coding of features that may seem complex and where you would probably assume that I would have to use a different technology to implement.

Here’s an app that I’m working on that animates the “filling” of a UIViewController subclass’s UIView color by placing “tiles” on the view. I start in the upper, left-hand side of the view controller’s view and add tiles in a column by column order, where each column is filled from top to bottom. You’ll understand when you watch this video:

When I’m done tiling, this is what you see:

You’ll see an extra bit of padding on the right side. That’s because, while my algorithm for calculating the sizes of tiles and the order in which they’re filled is innovative, I haven’t yet had the time to make it look perfect.

I used the Xcode Instruments’ Time Profiler template to confirm the fact that my tiling code is indeed lightweight:

As you can see, the UI and app in general remain responsive while tiling. In fact each tile animation very briefly uses approximately just 20% of CPU on the main thread.

Conclusion

I’ve made a convincing case as to the extensive utility of timers in app coding, so I don’t need to rehash this tutorial. While describing my code, I did show you some code that I didn’t fully explain. I wanted you to concentrate on timer essentials. Now that you understand the Swift Timer class, let’s take the time to cover a few details that are worth mentioning.

When we create a Timer class instance using the scheduledTimer type method and a repeats argument value of false:

You specify whether a timer is repeating or nonrepeating at creation time. A nonrepeating [false] timer fires once and then invalidates itself automatically, thereby preventing the timer from firing again.

If you’ve ever wondered how to start some task at some predetermined time in the future — like after your app starts — this is one way of doing it.

You should have noticed that, when creating my timer instances, I set the tolerance and RunLoop. I set tolerance to 0.15 because:

Setting a tolerance for a timer allows it to fire later than the scheduled fire date. Allowing the system flexibility in when a timer fires increases the ability of the system to optimize for increased power savings and responsiveness.

and:

A general rule, set the tolerance to at least 10% of the interval, for a repeating timer. Even a small amount of tolerance has significant positive impact on the power usage of your application.

I used RunLoop.Mode.common when scheduling my timers in a run loop. I wish there was a simple explanation for this in Apple’s documentation, but I can’t find any. The topic is discussed at links like this one and this one, but I can’t find anything clear and concise.

So trust my experience and research into putting all the pieces together that RunLoop.Mode.common makes your timer more responsive in terms of the UI. In other words, scrolling or tapping on the screen does not interfere with your timer’s timing.

We’ve covered a whole lot. I hope you’re feeling more confident about using the Swift Timer class. I’ve used them to great success in my apps. You will too.

Author: Andrew Jaffee

Avid and well-published author, software engineer, designer, and developer, now specializing in iOS mobile app development in Objective-C and Swift, but with a strong background in C#, C++, .NET, JavaScript, HTML, CSS, jQuery, SQL Server, MySQL, Oracle, Agile, Test Driven Development, Git, Continuous Integration, Responsive Web Design, blah, blah, blah ... Did I miss any fad-based catch phrases? My brain avatar was kindly provided by http://icons8.com under a Creative Commons Attribution-NoDerivs 3.0 Unported license.

Leave a Reply

Your email address will not be published. Required fields are marked *