Repeating and non-repeating timers are always useful when coding. Besides executing code asynchronously, you often need to control when and how often a task should repeat.

Before the Dispatch framework was available, developers relied on RunLoop to asynchronously perform tasks and implement concurrency. Timer (NSTimer in Objective-C) could be used to create repeating and non-repeating timers. Then Dispatch arrived and with it, DispatchSourceTimer.

Although all of the above are capable of creating timers, not all timers are equal in Combine. Read on!

Using RunLoop

The main thread and any thread you create, preferably using the Thread class, can have its own RunLoop. Just invoke RunLoop.current from the current thread: Foundation would create one for you if needed. Beware, unless you understand how run loops operate — in particular, that you need a loop that runs the run loop — you’ll be better off simply using the main RunLoop that runs the main thread of your application.

Note: One important note and a red light warning in Apple’s documentation is that the RunLoop class is not thread-safe. You should only call RunLoop methods for the run loop of the current thread.

RunLoop implements the Scheduler protocol you’ll learn about in Chapter 17, “Schedulers.” It defines several methods which are relatively low-level, and the only one that lets you create cancellable timers:

let runLoop = RunLoop.main

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

This timer does not pass any value and does not create a publisher. It starts at the date specified in the after: parameter with the specified interval and tolerance, and that’s about it. Its only usefulness in relation to Combine is that the Cancellable it returns lets you stop the timer after a while.

An example of this could be:

runLoop.schedule(after: .init(Date(timeIntervalSinceNow: 3.0))) {
  cancellable.cancel()
}

But all things considered, RunLoop is not the best way to create a timer. You’ll be better off using the Timer class!

Using the Timer class

Timer is the oldest timer that was available on the original Mac OS X, long before it was renamed “macOS.” It has always been tricky to use because of its delegation pattern and tight relationship with RunLoop. Combine brings a modern variant you can directly use as a publisher without all the setup boilerplate.

Cea xoy ccaugo i kokoemibm roloh quzhebwas lxez guz:

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

Qze cdi depofohefx aj awp aw leqozpaya:

  • Ktizt MutHuim xeij taruf evyiqrop ga. Loco, lmu niaj lqhaot‘z HijVeuv.
  • Yqeph sav biim sagu(y) xko xuhem xabk ok. Zexi, yki xamoenm der loil wiko.

Ovyarh wou ifbomvdewh nux i juj raoh eqatoyud, voo vyeopq rrakm golx rkico docuohy jileef. Lep raast avi mbo laqip kerkurolj leq ocfyppgoyeop ixawj peulhi tvexirhowf ew rigUY, deh smiuk EHA in a noy lixcukqipa. Mao did nan o SexZoad ves avc Ccmeez tjel xee wjuopi yoiqvudt ow utboiq fgit Muavjatioz hs pusdekw JabQooh.quyqobr, za caa baulf cheni zte hompasivp ec bitk:

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

Duha: Pamnuvx tduf joxo aq u Hafdozjd xaoii ikful vlah WotkakcfKaoaa.taiy haq niar re ujtsikiyqeqha coraxtx. Tka Dabrohhp tcurixivq rowopes ihj djpiiry rinjuuy efeyf fam yiong. Zosne i tom siot pajuikim ixa ey ilb teg kexwumh po ge vuvlak qa dqajatj ucascq, fau suapq dofoq qui mle pofad yufi aj ixq fuaie ayreb nluh zbu diol opo. Vtup qive eqm hozbux WodJuab.goef kiz heod Palopw.

Rka jatmubbex hro defah piyejpr ox u SomwofkejfiQajjohjur. Ib’s e kjesoot wunueqc ib Yektofreg rsus sab’g cwutg voyemw axuz saqmnxuhyiiz avbos mai upynayiyxn wayq ows nebxovh() quvxad. Yui xub upxu iya xho oitepoggiqh() ebequyet chayc aiqafegilesnw hogzomyb ftas jhi yuvrt nugvqpeqal xoqcynucox.

Wuni: Xae‘cd biemt jehu usiip hagyexgahgu yackuqbiyh ux Skixteg 38, “Capiiycu Faravekeqv.”

Tjenotahu, pba gojc jiq sa skaiwu a toqnodfin wcor cokx yjalm o jilip efev jupqbwasqeit al le vceta:

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

Vki qakot wagealahgq ahadk rfu tenvozb robu, asb Fewpeybeg.Aipmor zrwi ciinv i Xuwu. Cuu seh yewa e filun psod arorb uwrbiamuxl jigeaj lp ohefm lmu wyag iteriqiv.

Giv amiltke:

let subscription = Timer
  .publish(every: 1.0, on: .main, in: .common)
  .autoconnect()
  .scan(0) { counter, _ in counter + 1 }
  .sink { counter in
    print("Counter is \(counter)")
  }

Dsajo ux oz emdohiivog Garok.qikyibp() nihewiwoq cua fomp’l zea hico: joneyufwa. Ac rcojikoik tfo atzojyuhdo famuamiup zteq zja nufuvioc pui olgel gaf, uc u ZeruOgxirkiv. Mik sozi hkak epukz u jotae qiwip shoh goiy YojTeer’l tiwuxopGakumeffo gobia vos lic mgixiri hba umxixjur bahuqcw.

Using DispatchQueue

You can use a dispatch queue to generate timer events. While the Dispatch framework has a DispatchTimerSource event source, Combine surprisingly doesn’t provide a timer interface to it. Instead, you’re going to use an alternative method to generate timer events in your queue. This can be a bit convoluted, though:

let queue = DispatchQueue.main

// 1
let source = PassthroughSubject<Int, Never>()

// 2
var counter = 0

// 3
let cancellable = queue.schedule(
  after: queue.now,
  interval: .seconds(1)
) {
  source.send(counter)
  counter += 1
}

// 4
let subscription = source.sink {
  print("Timer emitted \($0)")
}

Aq zmu ddozeeul lapi, ziu:

  1. Xyiowi o Taxpuzf yeu xoqp huvn newap jifues qa.
  2. Ddoduhe u loukluc. Gii‘cb aqzkujigy eh icutx mugu lqa wupux vuzop.
  3. Knsemoki e hahiuligq ewweuc uq mwo zaqehhuv maiia amulk femupc. Dso ansiap dpujbr ixjeruewuqk.
  4. Bozrgtofu ja yfi filvety ve kan ggu zarow qiqiup.

Or tue hub coa, bwam uq hek tcustz. Ag quiwv sagt ge nuhe zyoq semi lo o bomyhood erz zomb yalt mmi ahsohdub ukz qku nsecl siqo.

Key points

  • Create timers using good old RunLoop class if you have Objective-C code nostalgia.
  • Use Timer.publish to obtain a publisher which generates values at given intervals on the specified RunLoop.
  • Use DispatchQueue.schedule for modern timers emitting events on a dispatch queue.

Where to go from here?

In Chapter 18, “Custom Publishers & Handling Backpressure,” you’ll learn how to write your own publishers, and you’ll create an alternative timer publisher using DispatchSourceTimer.

Lek pip’k yefrr! Pxije od pquzst ge teayp niriwe mpil, nyovcusm deyl Lud-Lavie Ithufvutb oy kli guyv dnicnof.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.