Combine and Timers

Ario Liyan
2 min readApr 15, 2024

--

In this short reading we go through RunLoops and Timer class in Combine Framework.

Photo by Ralph Hutter on Unsplash

Table of contents

  • RunLoop
  • Timer
  • Using DispatchQueue

RunLoop

In short

A RunLoop object processes input for sources, such as mouse and keyboard events from the window system and Port objects. A RunLoop object also processes Timer events.

RunLoop class conforms to Scheduler protocol that let us to use timers.

RunLoop class is not thread safe and we should only call RunLoop methods for the run loop of the current thread.

let runLoop = RunLoop.main
let subscription = runLoop.schedule(
after: runLoop.now,
interval: .seconds(2),
tolerance: .milliseconds(100)
){
print("Timer fired")
}

Timer does not pass any value and does not create a publisher. It starts at the date specified in the “after” argument with the specified interval and tolerance. The Cancellable RunLoop returns lets us stop the timer after a while.

An example of stopping the timer:

runLoop.schedule(after: .init(Date(timeIntervalSinceNow: 5.0)))
{
subscription.cancel()
}

Timer

The old familiar Timer class has being exposed to the Combine framework by a new API.

let publisher = Timer.publish(every: 1.0, on: .main, in: .common)

let publisher2 = Timer.publish(every: 1.0, on: .current, in: .common)

“on” argument is for the RunLoop that we attach the timer to.

“in” argument is for what runLoop mode we are running the timer on.

Note that the “Timer.publish(every: 1.0, on: .current, in: .common)” should be run on on DispatchQueue.main otherwise it may cause unpredictable results.

Timer publisher is of kind connectable publishers. It means it won’t emit values until we connect it or use autoconnect operator to connect right away.

let publisher = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()

Using DispatchQueue

In this approach we use a little hack and combine using the PassThroughSubject publisher and DispatchQueue.

    let queue = DispatchQueue.main

let timerPublisher = PassthroughSubject<Int, Never>()
var timerCounter = 0

let cancellable = queue.schedule( after: queue.now,
interval: .seconds(1) ){
timerPublisher.send(timerCounter)
timerCounter += 1 }

let subscription = source.sink {
print("Timer Value is \($0)")
}

Next

--

--

Ario Liyan

As an iOS developer with a passion for programming concepts. I love sharing my latest discoveries with others and sparking conversations about technology.