NSTimer

NSTimer – Repeat

The NSTimer class is a workhorse of iOS applications, but it is also complex, fraught with hidden gotchas and cumbersome to use. For example, when a timer expires, its callback mechanism consists of performing a selector on a target object, which doesn’t work with a pure Swift class (or struct, etc.) and forces us to use NSObject-derived classes. Also, the run loop maintains a strong reference to a timer’s target object, which can be unexpected and problematic. To break the strong reference, we must stop the timer (by calling invalidate()), but only from the same thread on which the timer was installed. And once invalidated, timers cannot be reused.

by a guest blogger

The core Swift team often asks, “if we didn’t already have it, would we add it to Swift 3?” If the NSTimer API didn’t already exist, what kind of API might we like to have? It would probably:

  •  Be simple to use for simple cases, with a clean, Swifty syntax.
  •  Be closure based.
  • Allow us to hold weak references to objects.
  • Be thread-safe for subscribing and cancelling timers.

Let’s write some hypothetical snippets of how we might like to use an API like this in our program. Once we’re happy with the way it looks, we can work backwards and figure out the implementation details.

For a timer that’s going to run one time, we would need to provide the time interval and the code that’s going to execute:

Of course we should support a repeating timer as well:

What if we might need to cancel our timer? There should be a way to keep track of a specific closure that we have scheduled, that doesn’t get in our way when we don’t need it, but is there when we do:

It might also be convenient to alter the timer from within the closure itself, perhaps in response to some condition. We might want to stop the timer, repeat it with the same interval, or with a different interval (not shown here):

A closure-based API allows us to easily nest timers as well! The below example counts to 10 (after an initial delay), increasing its speed half-way through:

Let’s recap where we are so far. Though the specific details of the above API could be altered to suit one’s preferences, we have created an API that:

  • Is clean and simple to use for simple cases, with increased sophistication available when needed.
  • Keeps the scheduling of the timer close to the code that will execute when the timer fires.
  • Uses closures in a way that feels at home in Swift code and allows for the use of capture lists to capture references weakly.

The following public functions are exposed (the source file contains more-detailed comments):

A reference implementation, which uses GCD as the underlying timer mechanism, is available to download. A few implementation notes:

  • The public API is thread-safe via a lock on the singleton Repeat instance (equivalent to wrapping the body of each function in Objective-C’s @synchronized {}). Consequently, a reasonable number of timers is expected to exist at any one time – i.e. not hundreds/thousands/etc.
  • The provided closures are executed on the main queue.
  • Like NSTimer and GCD, this is not suitable for realtime needs (i.e. don’t drive your game’s run loop or latency-sensitive audio processing with this). According to Apple’s documentation, “the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds.”

References

Class Reference
Title Image: @ sergign / shutterstock.com

Book Tip

Big Nerd Range Guide: iOS Programming: Excellent introduction to iOS development. Some programming experience is recommended.

Amazon