Replacing Foundation Timers with Timer Publishers
Publish elements periodically by using a timer.
Overview
If your app uses Foundation’s Timer class to repeatedly receive a callback or invoke a closure on a specified interval, you can convert these instances to Combine to simplify your code.
Performing Periodic Work with a Timer
Consider the following snippet, which uses scheduledTimer(withTimeInterval:repeats:block:) to update the lastUpdated property of a data model once a second, on a specific dispatch queue:
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.myDispatchQueue.async() {
self.myDataModel.lastUpdated = Date()
}
}
}Converting to a Timer Publisher
To migrate this code to Combine, replace the Timer that is returned by scheduledTimer(withTimeInterval:repeats:block:) with a Timer.TimerPublisher. You create this publisher with the Timer method publish(every:tolerance:on:in:options:). Every time the underyling Timer fires, the publisher emits a new Date that represents the instant the timer fired. You then apply Combine operators to the Date, eventually connecting the publisher to a subscriber like sink(receiveValue:) or assign(to:on:).
The next example shows how to use a Timer.TimerPublisher to replace the previous example. It uses Combine’s operators to perform the tasks that were in the previous example’s closure:
var cancellable: Cancellable?
override func viewDidLoad() {
super.viewDidLoad()
cancellable = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.receive(on: myDispatchQueue)
.assign(to: \.lastUpdated, on: myDataModel)
}In this example, Combine operators replace all the behavior inside the closure of the earlier example:
The receive(on:options:) operator ensures that its subsequent operators run on the specified dispatch queue. This replaces the
async()call from before.The assign(to:on:) operator updates the data model, by using a key path to set the
lastUpdateproperty.
Another advantage you’ll find when using Combine to simplify your code is that the Timer.TimerPublisher produces new Date instances as its output type. The first example’s closure receives the Timer itself as its parameter, so it has to create new Date instances manually.