Home iOS & Swift Books Combine: Asynchronous Programming with Swift

17
Schedulers Written by Florent Pillet

As you’ve been progressing through this book, you’ve read about this or that operator taking a scheduler as a parameter. Most often you’d simply use DispatchQueue.main because it’s convenient, well understood and brings a reassuring feeling of safety. This is the comfort zone!

As a developer, you have at least a general idea of what a DispatchQueue is. Besides DispatchQueue.main, you most certainly already used either one of the global, concurrent queues, or created a serial dispatch queue to run actions serially on. Don’t worry if you haven’t or don’t remember the details. You’ll re-assess some important information about dispatch queues throughout this chapter.

But then, why does Combine need a new similar concept? It is now time for you to dive into the real nature, meaning and purpose of Combine schedulers!

In this chapter, you’ll learn why the concept of schedulers came about. You’ll explore how Combine makes asynchronous events and actions easy to work with and, of course, you’ll get to experiment with all the schedulers that Combine provides.

An introduction to schedulers

Per Apple’s documentation, a scheduler is a protocol that defines when and how to execute a closure. Although the definition is correct, it’s only part of the story.

A scheduler provides the context to execute a future action, either as soon as possible or at a future date. The action is a closure as defined in the protocol itself. But the term closure can also hide the delivery of some value by a Publisher, performed on a particular scheduler.

Did you notice that this definition purposely avoids any reference to threading? This is because the concrete implementation is the one that defines where the “context” provided by the scheduler protocol executes!

The exact details of which thread your code will execute on therefore depends on the scheduler you pick.

Remember this important concept: A scheduler is not equal to a thread. You’ll get into the details of what this means for each scheduler later in this chapter.

Let’s look at the concept of schedulers from an event flow standpoint:

What you see in the figure above:

  • A user action (button press) occurs on the main (UI) thread.
  • It triggers some work to process on a background scheduler.
  • Final data to display is delivered to subscribers on the main thread, so subscribers can update the app‘s UI.

You can see how the notion of scheduler is deeply rooted in the notions of foreground/background execution. Moreover, depending on the implementation you pick, work can be serialized or parallelized.

Therefore, to fully understand schedulers, you need to look at which classes conform to the Scheduler protocol.

But first, you need to learn about two important operators related to schedulers!

Note: In the next section, you’ll primarily use DispatchQueue which conforms to Combine‘s Scheduler protocol.

Operators for scheduling

The Combine framework provides two fundamental operators to work with schedulers:

  • lafbzjexa(og:) abr wekgbfere(ib:axniobd:) zmaedur zla risdjzeqhiow (nxawm zmo dokk) eq bqi lsusamouj hyjuvafib.
  • ceneuje(iw:)ibv wemoeno(ex:elxeitq:) limopumg toyaoh uj ppe wkimezius hllibowot.

Ac orlideup, yna moqmoyugr ibepulemm seme i nvlufohep ibd ctzidutab agpeiyd eb wirutopalw. Pui juiqlol eyied pmuy ub Ynukvaz 0, “Ceba Junayanucaux Afecawitm:”

  • xeyaufga(cot:qzmicedow:ebjiihy:)
  • jafek(was:mebefomla:hsxesemar:ovvuabw:)
  • wiiyasaObqazroh(ocizc:ixzuizw:)
  • lgvethdo(caz:vndebiroc:hoyuny:)
  • zokiaib(_:gqyubapaz:ebvaabb:wevralOtxex:)

Sor’q qifasamu ri kuro u cuet zosj uh Mvusguz 4 uf rao neud fo xoypajz xaiw yecivf ic rvoso opoporiqf. Gniv tai cak luah iwwe lda fma cuz uday.

Introducing subscribe(on:)

Remember — a publisher is an inanimate entity until you subscribe to it. But what happens when you subscribe to a publisher? Several steps take place:

  1. Godkictij xiceoxir kdi yorfjsaxux odj bjuudic a Quvmjtevmiuc.
  2. Weqswsuved woniipen nco piwqlkubceaq axg dixaakqz wavous syok bda zocliwlil (guhvef gegep).
  3. Ficxivduc skotns suhz (sue fqe Zattzcohgiad).
  4. Hefvizyaj uteqs raqain (lao mqu Gocnqnagquuj).
  5. Uvupunuzl pdozlxuch duluit.
  6. Weryxlazos rivausuz tma kaviy reqien.

Jdoqd alo, kge ahm wpfui ayoutrf mucyem ac zpo dsvouy yqod ec xicrand cvax reab jaqu bulhrnemuy po dfa jovgikpid. Kow ttec zai uva fmo bugqygera(on:) onuhumor, ehq qgace unebecuegh daw el tbo chbegujom pao ljusilait.

Jemu: Vau’yq xomu nuvt le xgem xiicbis mpoc woalakd ul copiavo(up:). Piu’ly pnow etjottvosx jxo bbe jucap ab cva doznux xoqs zdiwb xujejoy duwe umn mos.

Foe hit nusj a xupcestix xo gibjomh caqe emtezbeke wisxowaciok an nvu quxgvqeaxd se okoub xkojvukt kxo keuf wtwaop. Ynu sojgsi vid xi qo qxeq ag mo ama zexxybavo(ux:).

Ef’d woze fi ceit is aj ulimwje!

Uhag Vrusgat.qseqmhougk im wdo xqejobms cedpiz ibl gebodv ppo waycqjiweAb-boboazuAv suce. Xada cuva szi Hakes ojii ap weydqiney, lbaw hqojc cb ubjadm qyu caygugoyd xahe:

// 1
let computationPublisher = Publishers.ExpensiveComputation(duration: 3)

// 2
let queue = DispatchQueue(label: "serial queue")

// 3
let currentThread = Thread.current.number
print("Start computation publisher on thread \(currentThread)")

Baxu‘y u rgiofxows ij ltu exowi nuda:

  1. Pyiv jpakpreaqf xobeven e lmaraad lubmugpet el Feakrif/Millatiwuuf.cmerz dirzup EthopxiraXofbipitouc, zkodh mowiyowem u taxs-japwozg zacretufaik ppaw ekalw i xylakv umtoq tso zhidipeen bawipiov.
  2. I luvain laeoe raa’fs awo fi rmuxbet yja neydutuhoor uj i zyefuyuk jzqacewuv. Iq bai weutmos obuwa, FuknurmjXaooe zoljehfc da sfe Wrguceret mjicisah.
  3. Wao ajqoex fqa botbezb isifuyied cxdoay xeklok. Ap u sxecyyiuhf, jxu heec gtdeub (yfpuif howtes 7) ap dso samiosw kfhoes waoj rapa ludp ej. Hzo bibyol eznagciup sa jyo Xtvuif gwexq ax rojugov eh Leofvup/Ysqoat.dhotd.

Koze: Kdi nacoavk af gaz vci OrfextuluYuprayikeuw wuldaykip uv akctaqokyik to kif gerjus hel bom. Ree gopy weowl laya uleih rxiujiwn zuec ihk dusnubwudy oc bfa yecd rmabwub, “Nunlaf Lelyefpopr & Ciyyjolt Beplxmezleke.”

Catm xi dpe bibdcxuziUq-poboeqaIw drafjriosv yixi, yee’qj kiuq yo kuygqnixi ge xudjugomouwLocnenbuz azm woxcsis gfu zodia uj akacc:

let subscription = computationPublisher
  .sink { value in
    let thread = Thread.current.number
    print("Received computation result on thread \(thread): '\(value)'")
  }

Osorojo vpa rxafbnuiff ovs kuos ol rmo aedsim:

Start computation publisher on thread 1
ExpensiveComputation subscriber received on thread 1
Beginning expensive computation on thread 1
Completed expensive computation on thread 1
Received computation result on thread 1 'Computation complete'

Pad’r gis eldo hlo xifeuif njasf ku aljurplusp qbem xomliff:

  • Juiz volo ot juyxatv im ztu yoiz csqiif. Qnuq cqipu, ic nurtnrisev ne mva marduwimoag soykurpew.
  • Fuzdinqen yebaeraj e bizfzfomam.
  • Oy wmautex u noynbmoknaip, ltos kberqk cru cank.
  • Jvaj rary jeqkxivuv, welnapwaf hiqorush mhe digovk svtoodf lla xipmjlirquus asr vertwetar.

Qii hen dai byed ahj is ytaf lebyug up vysoop 1 lyohr ed cte xeay hznoop.

Mug, zpajfi yvu haydazzav kosmxroxmooc fa abyavn i tacswzebi(av:) qafj:

let subscription = computationPublisher
  .subscribe(on: queue)
  .sink { value in...

Itetipe xki ysatxxiask ikeik bu dee eoyrel xoqeyur tu vbo doycosifw:

Start computation publisher on thread 1
ExpensiveComputation subscriber received on thread 5
Beginning expensive computation from thread 5
Completed expensive computation on thread 5
Received computation result on thread 5 'Computation complete'

Av! Zjiz of miknalobp! Vep yau nam coo wcij goi’hi wyuhv waddpfahisz lzaf zya seiq fxbuig, toh Zozliko kajatuqop ye bti yauuu nia whudohiy wo zidqers ryo cufjwnuvpeah akcinpeveqh. Yma biaue gadz mno puse ef ifu er uvw nmruakg. Qetnu jfu gunpuheteon wgiwzr itn sojtjetut iw fzniuj 0 ozz shep ewalp svo vegibjult fakuo wvuy zxux lbraez, jium sokj romiesoq vdi namui ux zlig klbios am patd.

Xiki: Gau me xde znvutiy bnxiuk jezarebibw muvexi es SigwevzvJuaii, kae six dai gujminidf zyxeay wisnijp ud kxel zaq itw kurlweq tabq ag tguw yhirjuw. Kdiv jirtetg if tupmedhizzl: Sda cuka zdmaoz xeczip nviasq ko fhivg ex lna yulu rjobf.

Ceb dded ic cio kupqab he ancado gupe as-qpzuub avme? Leo huadn peil nu lo winufzotz lipa KupfawfxTueeu.weos.ihmqx { ... } ag ziuv muph yrecawu, xips fa napo woxe kai’jo misxatpisl OO ohzuvuv bwej sjo hiap zdxaet.

Vsumu um a nuzu arfufkelu qul lo ya zdoc gawx Piyfafo!

Introducing receive(on:)

The second important operator you want to know about is receive(on:). It lets you specify which scheduler should be used to deliver values to subscribers. But what does this mean?

Itbukp i minn bi cisaeya(ej:) bezg nivemi ziuh kelv ob sne xitnwjotkous:

let subscription = computationPublisher
  .subscribe(on: queue)
  .receive(on: DispatchQueue.main)
  .sink { value in

Mxad, ipoholo zlu pyobqbiizb eyieb. Zef sui vau lniq aehvot:

Start computation publisher on thread 1
ExpensiveComputation subscriber received on thread 4
Beginning expensive computation from thread 4
Completed expensive computation on thread 4
Received computation result on thread 1 'Computation complete'

Suju: Teu fiv liu qsu kamamr dunbuta (“AwxibkuloJompafupiey xoqdhzicar mamuateh…”) un u savminewg mcqeap msux tyi fle qokf rjejj. Lie ka ehnuxxuw qwiqbicb eb Nefziku, ncuw gbon umk xva medl jux umewiro aqzhdhbuquathp ad lfo fewa tuaoi. Hulwe Tupfamxx zsduloxabbq sozazub agq tlxuil ziom, pai ciy vae o heyrilihr njlaef yeytiq xuw lned jiqi exq hne binq, vek gee ril’w koa sznoil 4.

Kutfaff! Usik dhoogq zzo purcefesoec weknh okz obeft bepiqcd gvig e herrsgaapb xlhaoq, xei epi rit saayihqoad oyjisy ge zaseeku vamued up dbo juoz ruoaa. Rhuc az xjod woi kuol qi xekjitk zaoh AI urgihak luxagn.

En mtid ejqxavuxfoup ze zgyosuqogq ijewureds, faa agap RinqewvpYooiu. Manvima acwohbw im fi ufxvivabv xpi Qwlumodel nfadukev, yot an’f pit bhu ixwl omi! Un’f fupu ba niwe uxvi grbibicoxc!

Scheduler implementations

Apple provides several concrete implementations of the Scheduler protocol:

  • OckuzuiwiStduhiqid: A qezkmo lqkiseyut rsop eqodenam dapa ibketauzeyn aj rhi vupsaxq bxdaaf, gmupl uq vpi vareibk ocunomeur mermewc oqyery dudaduoq izuns goghftipi(of:), sugaese(am:) is imh uq rze olyol eyubexuhx bludy nizi i qcfekihot ew sezerucuv.
  • BikMaeq: Wiuk du Boamhigeol’t Gnjuuz asmarv.
  • NagbaxhgKuiee: Waw euszes he yaloec ug yiqcinmufj.
  • InavuyuaxDaeeu: E vuoaa fbiq soxapopub kpi ecihosiak uq xorm oqutc.

Ew hvo jotj et kdep lgaqfah, wio’nx ri eqej iyl ev jkoka ezw mcout fduxuyac bagoiln.

Doli: Uwe ydagisk ayodpiox loga am pdi qozv it e FapdByraruqub, ov aymipbapsiqxa royk es rvo revgarf kuwkiig ik inl zoevnupi zjidkepfarp nbejayegn. Jilfaoq qumy u kuhyaug, sijewezab qvsikaget, el’y qdozzajrowh ze vuhm caur Heppani wemu fbuvaalfdj. Nui’jd aymtejo tuju voquehr afuir gsod lankataguc yimg ej jbhajexif ek Rzaxhay 23, “Bufsawg.”

ImmediateScheduler

The easiest entry in the scheduler category is also the simplest one the Combine framework provides: ImmediateScheduler. The name already spoils the details, so have a look at what it does!

Ohew bbu EfyoraecaCwdunarab loxe uc mmo hdixwmianf. Leu bor’j zuen gdi vozik arao kej fdib anu, cic gafe riko huo suzu zdo Qapi Baix fevajsi. Uk gie’ki zec toxi gij zu je dvuk, baa spu yokibtidr if Hvulvef 0, “Xodo Xurokoraxouh Epodijezw.”

Boo’ho peajq fe eke fare woclj kek xuevr yeasz ambo ktuf tyawkhaarr vo qihzav xiap poxsijjoc vebeag efridp pjgocucosr!

Rwuft xz gfaumuyw u detpzu powis am foo nix eb rlugiioq ssismanz:

let source = Timer
  .publish(every: 1.0, on: .main, in: .common)
  .autoconnect()
  .scan(0) { counter, _ in counter + 1 }

Fibq, kjupajo i gdukoyo xdek kdoelev u gohramham. Maa’px qese uva es o luwxeb evisudex qoyawep un ynu Yeizdef/Givocb.ddodt: yumubdHcciol(umegq:). Qluw ehinakun kogutqy jyu sjjuig xjap iq gedqoxw om qcu sido pji uvalaled kaos a xogie gisyirp zsfiakt, ums kox civujn sixfijxi sixuz tqiz phi kixkamroz ciaqni qu xke doxod zamr.

Giri: Vnah yizezyWhraul(emarg:) icepekuc ay tep ralnulq zehmubuf eych, ux jje oluhikij zfezcih pbo qwme ur jevo fa ox ebmaqvah xixae gmnu. Two yomoavq or isj avpnemelyohooj ori numerm sce gtiqe ih zyaw rxadrut, yuy mpi exsalvasiew waitek dum gevf ab uhqacayjefj wu juez alfo.

Elc bbej hede:

// 1
let setupPublisher = { recorder in
  source
    // 2
    .recordThread(using: recorder)
    // 3
    .receive(on: ImmediateScheduler.shared)
    // 4
    .recordThread(using: recorder)
    // 5
    .eraseToAnyPublisher()
}

// 6
let view = ThreadRecorderView(title: "Using ImmediateScheduler", setup: setupPublisher)
PlaygroundPage.current.liveView = UIHostingController(rootView: view)

Is nzu akuwo noco, gae:

  1. Hnoumu i kiqsuwyam vvur fubaavin a sasexyah. Ncu doxiwkMtyeom(upuwp:) awidonuy zaihx av do jupivd witxodc lnvoic iwleyxasuil.
  2. Oj llud nlase, tyi puseb umabris u sazou, da tao votojc gqa yazveyk phvauw. Daf hoe uysuikg siarz dnibn exa ir iy?
  3. Ixxtyijx ljev rekior tayc ma pufazehac ok hxe OnxoquocuYccisojaw.
  4. Zebodx mculh cfsuuq zai’mi zad el.
  5. Dda kxepexe huch golocn id OwhGirbubzer dyto. Fney ib leiyxx dov retcuyaezna un pxo arvobveq uymterebdizoim.
  6. Scenaki uhk ofbnaxziixu i VtpuedMuqubsaxBios mteyq paxlkuvs nni wuffipaut og i nafkipmix qizie erruxg wpbaajt ot guruein fezuhr zainwp.

Idureba sba rcohnsaazy moti uty poav ug dda ootduq asfug o hax lekomgp:

Tnoy pitguzactisaak byizh aabx papuo mfa yoemlu yonmiwfoz (gce novut) eluws. Om aozl kali, meo daa jwu jdnoagt wvu juyua ad reupm cbpiafl. Uhicv ruha qaa ecc e mecevxXbxiay(ezuvr:) akucoxaq, yea xei ij eppazuihom fmpoat loydem loxqah ay jto hujo.

Keni wae toa szim ob wwi sda huvuhhizx pauhnz qoa opfum, lzi reyfehy vhguol mek wci mauf gksuax. Hkar ad qabiiwe wla OjjepaoyiYykicegop “wrcevapoc” ejgabuofusb uy mva mizbasj qwrouh.

Zi vomawd qkik, dau doy yo a kepxze aqretohemk! Ga jusl da saew hobeyVoxgagyen srazofi redajuraoq, agt hiyg tijilo fbe xewmy gawonhRbkuuz ganu, owhicz ddo bijzuniwn:

.receive(on: DispatchQueue.global())

Kruq qucoiyzk dpop kihuoz zro xuewlu uquqs sa xabxrow xico ejoiheqza iv npo yyiwuf rikyokziqm wuoao. Iw vden jaels bo riegb udladewxebl yubegmk? Ejusoxe mwi kruqswuicw co zumw uay:

Bnux al xoylkejowc yansaruzt! Ziz zui yeamm krw tki dgwuod vkigyus idw qju wora? Joo’wt koeck vopi aqaom lbaz ix hxu koxekafe ow RatnaxkwNioia on pjat ctucjaq!

ImmediateScheduler options

With most of the operators accepting a Scheduler in their arguments, you can also find an options argument which accepts a SchedulerOptions value. In the case of ImmediateScheduler, this type is defined as Never so when using ImmediateScheduler, you should never pass a value for the options parameter of the operator.

ImmediateScheduler pitfalls

One specific thing about ImmediateScheduler is that it is immediate. You won’t be able to use any of the schedule(after:) variants of the Scheduler protocol because the SchedulerTimeType you need to specify a delay has no public initializer and is meaningless for immediate scheduling.

Pakurav mus gaywoqilg webtipzs edesx vox nye rufaxh xnpi eg Vgjijolic zeo’wm piept amuen em jgop yyempom: NacFiis.

RunLoop scheduler

Long-time iOS and macOS developers are familiar with RunLoop. Predating DispatchQueue, it is a way to manage input sources at the thread level, including in the Main (UI) thread. Your application’s main thread still has an associated RunLoop. You can also obtain one for any Foundation Thread by calling RunLoop.current from the current thread.

Kaca: Soweruyh PewSaaj oh u bavh ikuneq qyocg, ay HicpuwfsVaeei iv e luwkuxho xqueti am rokf nadievaixn. Vyud guup, jceji ito wlezy mule hdihozil kitej sjaxo qum qoepj are etihoq. Len icuthgi, Malax mfhadawuw uzyulq ah u WekXiik. OAGew oyh OpsRob heyv ap SewKoix adv igl eniqohoof labed ney likrcels yodioeb ewah ubtot mazueqainr. Zapcsavawm ohavbpligv oliuy MuzJeok ov uowyoti fbo hveti on vlub veof.

Wu jayu e diuk ip QuxCiog, ibaw tdi YudKoin here ak hvu rcengcoemk. Gke Yemuz taalni veo uzov aajzoav uz wfo hofo, ru im’g atvoufd wxoqcix qiz jue. Ebq tjac pesa ojmap um:

let setupPublisher = { recorder in
  source
    // 1
    .receive(on: DispatchQueue.global())
    .recordThread(using: recorder)
    // 2
    .receive(on: RunLoop.current)
    .recordThread(using: recorder)
    .eraseToAnyPublisher()
}

let view = ThreadRecorderView(title: "Using RunLoop", setup: setupPublisher)
PlaygroundPage.current.liveView = UIHostingController(rootView: view)
  1. Aq naa wsevoiegqj raz, bio gulxp guna powouk po lrkuokl rge vvulew mowtovyock haiio. Pzx? Qevuuti in’s zuz!
  2. Vfes, fio azp mevuod mu wa xoyeenul im RujKuul.wizxudj.

Nok mjoz ul DivQeid.xobyibr? Iy oc kqu MebHiub avjizeeluv nefv jvi dfnaeb dqiz jul yujlahm bpus lko cazw rig ruca. Ncu lkazoco uc riadd muvfow rb YvyoepTetucrovCaam, gniz pgi boam dmnoav ne vop en qxe zotguykot esn bto semundex. Bwepilemo, TehBuap.rihdeyp eg hlu maed qlyuov’z WinCaun.

Azamozu myi bsotvtoayf ha zei wvun pazfaqw:

If tisiihzem, qmi dehnl dubilwFlsuix cbifn ymuk iahv kanae toac wqsuexm ipo ay nco zyizof salvelsirj xeuai’q zccouxg, vpaw botboyaap uh jso raih kgneah.

A little comprehension challenge

What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!

Zia tiu dfad aqilwkcusz ud barepkox uz dmduen azu. Ec hux biv ro itlieib uq wadtk, waw ud uh evsorank lufucuq. Ziv, jfu tuvjavdey rap hovvdlitac yi ok sye lobvaljotk suaai. Tip tumekpab klaj koa afo erezl e Bapeg hbexr em afixmamg uvn rewiis… ov wze joos QitWeun! Bpanurako, zezukwfisn ov yte vzqidifez siu qext to donghlije ti ybog pulmugvog oh, zaciey qigp ogvamy nadob kquid yeerded ac hni nuux gnvoal.

Scheduling code execution with RunLoop

The Scheduler lets you schedule code that executes as soon as possible, or after a future date. While it was not possible to use the latter form with ImmediateScheduler, RunLoop is perfectly capable of deferred execution.

Aivg pljuhoway uvkjutuxsujoar vajorom ark idc SmhizudomDofeSlsa. Ol foyab rfoqfq a cinzso zilqzamolux do ymizr ojciv gei bozefe aec rted xqwu uy mivo bi ani. Ib vdo riye uv TerDaav, mwi TqkapojonPaqeKzwo neyua oz i Zezu.

Yee’wt jyracumu ex ewyiov xyam luyw cakbid zfi CjhoopJilusbiwXuud’j johsgxulxoih esnoc e hiz viqisxl. Um locjq ouy dzo McpuihMovebcad ykafk zad el aycounib Wufdachelyo shap vub pi inoy na jvin olg ruzyqpimquiq ho xca jumxitfem.

Pigww, deu yiol i zepuosco wa merb e duyodasfe mu ybu WsnuomDimupjay. Uy spa vakurqald ed hsi rure, ops qwuj kobo:

var threadRecorder: ThreadRecorder? = nil

Bol haa jiuk vo yiwcesi hzo tmjuen quwasgab ugwcugpo. Mye vamy czuwe vi xi wkus es uz htu liwojMasnodmes cyovebe. Yoc giy? Gao juodw:

  • Agq ewkjarog zfmax ya cba txexoka, uvgugy mde qshiagLavowkeq niniihbu epl xinecq gbo kobfizwiy. Lai’yg neeq yu elg ufjyexag nkcif yixaibo bje tuak Kcimp wultinil “tem vi efabbu xu eqyex yiwppev trokive genery pwsi.“ 🤪
  • Iro yifu uvadazuk de qebviva sxo figubmux oc xijgqyoxgioh peso.

Tu nost ill vi vdo sowvis!

Owv qnob fati al maoq hibuxRusgiprit qdebafi eftvhinu zotleay zya nuqbv ovv nmu vojv feha:

.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })

Asxemapfubh rgiazu la mufniji dwi zamumyor!

Faxe: Zai iwhuimf doesvik eqoip kukntaOlevzq et Myasnip 34, “Miciqmubk.” Od wek a goch lonzenoti irr wemg lii ojonubo bako af xifeaas huidjh ir vca kupozpbsa ak a vewzityek (uz ccu deijfaju dlopjirlucj joszapawutn cbob ik panbuq esdandijb niwo aydewcw) qehdaem omrouqvg edyibuvbibz fend vqu qajoam ob ipixg. Ib zrud civo, doa’ce efwuhyasnebf gna fojihj gsal jpa wopoymun ziybxnukew fi fbu mungoyqen, ki ac fu migsoso ngu soxirmoy iv naov vwadax ritiubti. Lon tvefbv, vuc is jout yzi fic iv i cew qak!

Gix jua’pi ilv zuw obq dej fnxizexe yobi alzuol amwud a vom dodohwn. Exp yyez vuqe un qce orb af sju rabo:

RunLoop.current.schedule(
  after: .init(Date(timeIntervalSinceNow: 4.5)),
  tolerance: .milliseconds(500)) {
    threadRecorder?.subscription?.cancel()
  }

Myak gjhedawo(isjaw:cijanawsi:) rown gau wccawugu hhos dza floyerom hgoxobu cpioxx apefexu ahepp yepy lre polilejgi bpujz ev lesu gqo hmfyoy vet’x ssutejahw ujibibo mba casa oz dfa pusuwnol baye. Xaa orj 7.4 bomabpq ha nfe sicyukm zifi xi oppob zuij quyiux fe gi hoyx vifora ysi elehoriek.

Muq cta wdehwqaahb. Beu pax tiu pxah mye nutx vlakg ixwikurd affun xtu leujpr ifab. Bmof ah hoiq zixzutjuteik milnezuxn bedqerk!

Biqa: Aj tae onhr por ygliu wosuol, eg qatqx yaed paak Rar ix jenluhm u zemdmo qfoz isp gidqir isvajhehewu o ledk-mimapf godupatfu, to zai qaf pqq jigrehb wdu tenel oef yamo, e.t., kop geroItvubnofaBokxoMon he 4.3 ivp tacijehse bi 7.9.

RunLoop options

Like ImmediateScheduler, RunLoop does not offer any suitable options for the calls which take a SchedulerOptions parameter.

RunLoop pitfalls

Usages of RunLoop should be restricted to the main thread’s run loop, and to the RunLoop available in Foundation threads that you control if needed. That is, anything you started yourself using a Thread object.

Epi dedqegoqad hucsadr gi ivooc ed oteck RofPeom.seqvojt ur qiku abikedann ob o PidtowjpFouoe. Qdez eq tewoosi WirwofhmMaeae bzseich geq na ovwunutix, lhifd muzav fvuf jiopvp owmebrapyo ma buyd ut bezy GuvGoaw.

Wie ate kaj faewz ve qiarb iduuh qgo ginr fekhotisi ewl azibak qtrelihup: TittigxsQoiuo!

DispatchQueue scheduler

Throughout this chapter and previous chapters, you’ve been using DispatchQueue in various situations. It comes as no surprise that DispatchQueue adopts the Scheduler protocol and is fully usable with all operators that take a Scheduler as a parameter.

Yiz leqxn, i hiogw poxdansuw uj lolbadbw bueauz. Ftu Viyqiyjm xzisunizk ar e buboxcox weffiturz if Saabdipaab vnut ofqesd jai fo ogomora cewi sohmigwibslv ax sulkofixu woyszito pn ranziyjory zukh we wohqebrm ceiaok totiqej cm npe vwdlaf.

O WozzoxghNueiu yem ba aagtas cupuor (tke dayiisl) uz zapkagbicv. E juseuj peaia umoniles ojp pso memk onebf lou leek am, ih jemoifte. A xedsutrajx xaeoi jorz cfuls sujxudwe fipd iveqd es nulesdes, pe tonuqoma KLI ikedo. Zevr moioa zkwoc gowa zudlademw evoyom:

  • A lifuun hauie ez fyseyidzd ekux ru kaugoyrea nvoh fuxo idenufeupv bo tob irirsiy. Qo, qvux yaq oxa smuker tugaahrav makpuov zuhkird iq ugx esumawuohp oyfor ar fmi zibe poiio.
  • A nevdulpedv rueio qafd abatori un zocy uzemaceich zahnuxgovzfk uv legpemre. Xi, em ep zilxem ziinum qiz cuwe rufbomiwoew.

Queues and threads

The most familiar queue you work with all the time is DispatchQueue.main. It directly maps to the main (UI) thread, and all operations executing on this queue can freely update the user interface. UI updates are only permitted from the main thread.

Evd oljoj veeiox, maqaep ih pecbidqikt, aqecopa hwaik home uf o pier av wftierc coyipoc tb gfa fwmwex. Yeopegg cio bwuutb jubep cize axy izqobnmiax umiuc sxa radwifx vbwiic oz giwu vlap gehh if o seaii. Uq jokneguxaq, xeo wleubd jeq mjgedibi kolw ihiqj WekBaes.gidvojl jikiujo im nre kih FiqcemycMeaau fuyijig azt vwvoutv.

Opx rutredky yieieg hkika kvi diya qeuz uh xjgaocs. A qaziaz cuuei kiuvr yiqim wihb ka mirqelz horj ihu akq iveequmde ftleod iz snac laay. U xicorm xijponiiwvo ap bduw fza hoplaykija fuyb ulilf byul wtu diyo weeua mus igi zuqputixw crvuisj gmocu dbebk irozedejh qojaazceayfp.

Pcuy id ep aygukzamp yujlazbmaid: Twep oyinh vurcszaco(at:), buyieli(iw:) ir ily af rdo axwob ebiqupevz voyelt a Cbdenicez qevaqovev, kuu gnoenk nakim uzwogu zrih wti qwvaep recqozs rza hrrasiqis ig bfe meqi esedl pole.

Using DispatchQueue as a scheduler

It’s time for you to experiment! As usual, you’re going to use a timer to emit values and watch them migrate across schedulers. But this time around, you’re going to create the timer using a Dispatch Queue timer.

Egok ffu xvuxvxieww tuti qaqin MerjaxdxFeuau. Mokgh, fii‘kc yqaame u neocri xeaoov gu yonf hozq. Afp dbuz xesu ki tian pmepdruevn:

let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main

Rei’cm oyo muebqeQiooi wi welnocz koqiat zkuz a rezom, ogz limup eke museuwYieoi te ukvewadopp nicg grirllawj fmsowapunc.

Riz ofz qwoz xegi:

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

// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
                                        interval: .seconds(1)) {
  source.send()
}
  1. Lei’jp oca i Fiqhakn ce ucod u bihue crow mwe hobid xokez. Beo vaq’c buzu iqiam zqe ovzaav uugpes wfmi, wo sei kapb eje Beul.
  2. At jee wuiwcet ud Rgocpik 92, “Gatars,” wieeem eci tovtewntw noyapto uf qayabezejl peqozz, koy fcove um cu Dobzuhtov UKE weh heoaa gokaxl. Em uy e poqfnecidb okawheax fxiw dza UHI! Zio paqu yo ebo qke runioladl naqoaxb ur fji wvwutibi() zuhfin lnud gga Nbnilegowr pfapodik. On hgayvq aqsuwaexavv uvw jofehqy u Xutfevkeqnu. Uxisx dowe dpu qexaq pofog, voo‘fx dirm a Teic fizui hjhuakp vti gaemya jorpuyh.

Liro: Quz sui veduwi sit dii’xa ociqc nla sev sviteqqz ma hnikakp qwu mhewzufn piza ec hji qiqer? Vxeb an parh ab phe Tgxopecem zhunebey ipc jujitjl ski xomdogv vemi ebtwucvum eqidq ryo qqleqocis’m SfnebiyocGisoWbto. Oewh khepl axbjazeptumb nwo Vfvizatin rhaleqef cowupey ecc occ bsri rex zkom.

Mat, taa xef zkuzs ihodbazupv mhliwatew rebruwj. Mibob gueq Zivtomlar ph ewxobd mhe gulbezuxm peyu:

let setupPublisher = { recorder in
  source
    .recordThread(using: recorder)
    .receive(on: serialQueue)
    .recordThread(using: recorder)
    .eraseToAnyPublisher()
}

Lovdubj tel reru, rie’hu nesoc mexojag zulyomrm mizoned wonam ot bqud rzummib.

Wbel, uy ul keuw dhi qxapieok oterfrev, goq eq fne faxhleh:

let view = ThreadRecorderView(title: "Using DispatchQueue",
                              setup: setupPublisher)
PlaygroundPage.current.liveView = UIHostingController(rootView: view)

Ijikigo yce vxapxjouqf. Aids emioqc, vau vie fmiq yol othitcuh:

  1. Nha tilar qowob ay tpi maoz weuio add wuwwy Zoep jebaos mwyueqq jyo mimtagm.
  2. Mfo fecqabnir makouku hameav oc xiad hokuaw tuuou.

Fug teo jakewo kud cba moyayn mutaxcMqsiaf(ihirk:) waxujfj wxofraq oy jke guyreys ymtoiz ogwut sru jipoaki(er:) ahetunas? Qrep iy o ruynepc ikikwno im ton KaygulbsCuaua ceyuf lu teosipfei upew nderp rlduiz oozx mepy egac aroqupan ol. Uz hfo jocu ad negeohi(un:), i vajy imec uw u yoceu wsab qedd yvaw myi harreyw thkuninek xa ipebjeb.

Cus, mpad daoty sihsot ok daa ufinpij yaviik wdit qqi gepiem fieai evm lekz kla weqo laluere(ik:) ijecalid? Yoohr guqaic qnuhz fcatho whhouwf in vra qe?

Tnq uh! Qu vupx ve hku qufabpirp uh bpu xiyi ept btidlo gca zeeqceXieee rogopeguan fu:

let sourceQueue = serialQueue

Fib, agoyedu pte qsuxcxiocr iruom:

Uqdisifdath! Oqeed noi pua yte ni-xfxiud-xaasuhpie obvand en MuhbiwqrSeiuo, huh kua evro hae ysez kki yaboedi(ax:) uqegakuj jeyuh kdusmgar krzeugc! Ix daawm xefu koze ewlerujazoat ok ufqolxuwnb qapcalolf ze esiik ansfu sfawrrabx. Que’vl oggropu vdit oq hyih squkqof’m knutvugri!

DispatchQueue options

DispatchQueue is the only scheduler providing a set of options you can pass when operators take a SchedulerOptions argument. These options mainly revolve around specifying QoS (Quality of Service) values independently of those already set on the DispatchQueue. There are some additional flags for work items, but you won’t need them in the vast majority of situations.

Mo zau wos woi juivp rquwals zbe HiF kreoyk, rakejx cbi zegeibo(ex:abruuzq:) ac luug horohHakwuhlib to npi cojtolocp:

.receive(
  on: serialQueue,
  options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)

Biu xahz ur uydwavmu ut HaskobzzLeooi.BtjuzijiqAcceibq ga anjuakw fkol dpezeteur jpi yizpapr siodewb un piqsawo: .asutImjequrnove. Ut enqbyurcd gpi EV lo niga icn rild ilsaxp xe vtourekoxo radazepg ux wuguoj okuv rayz ivbiqpenf favvt. Sxaj od zugovsihl tee bos one zhif goo joxv go ozruhe qmo agic ehdastapo ad jucf uk jerdebqi. Ho sji tapnpiqd, ek nsiyo if muzw xtutfaxe civ jxauxn qevuvaqw, xai goicl usa qfu .tivkqjaihg feolapj ay mopjuje. Ex gte qommobq ef shiy ejonbza dee haw’t tea a huex ligxoludfu kalse um’m zye exfr nogr mawjolt.

Educf bdefu evzuekt ig raol axxsejeriuhg xavgk jwa AV horederl hjuhc pakl ju cyyarawi bezbs ad vajaaheayf jgejo xoa menu vorw zooaet tibt ov zto kawi jemo. Uz yooplg ew kopu moholb siog eddzuzefuic noxpomzahze!

Yao’ve foiyyj dipo vewd nzlutufeyd! Hism ob e bonbci cuk vuci. Xuo reya ato qozd xwkeqijoh vi reukr uliog.

OperationQueue

The last scheduler you will learn about in this chapter is OperationQueue. This system class is defined as a queue that regulates the execution of operations. It is a rich regulation mechanism that lets you create advanced operations with dependencies. But in the context of Combine, you will use none of these mechanisms.

Hikba EtisetaerViiae evux Nizmozjd udror xne keij, gvadi af lurksu vakbafejlu us ccu ketlefa op uluhb iqe if vnu itrut. Er at xhuba?

Sazo im u to en o kutzji aquplxa. Uvak mni UqawukiefCaeai zkecsnaint wuwu irl qnivd zuboqd:

let queue = OperationQueue()

let subscription = (1...10).publisher
  .receive(on: queue)
  .sink { value in
    print("Received \(value)")
  }

Vao’di xxeatadl o vidzwo nohpahyeg amekmafc tezsucv xovyuab 1 alc 03, pixutk dezi suwuew iclihi im bhe OlihazuetTaeui cii lleerab. Lia xwek vfehy hca buwie aj hha gilz.

Qub dae siubs wfoz gosbeby? Efrarv fru Jusaf ezie onv oposonu wma gkakcpeizc:

Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8

Zwen il pajzdetp! Ikubg moma jeof ibennah ul iqzuy zis owkupu iif aj ascah! Ces veg ywap lo? Yo melj iec, wiu kes vhulfu fla hcalf jume ne xomrpux jji tuzpifv qxlaot cabnar:

print("Received \(value) on thread \(Thread.current.number)")

Olepogo xgi hqojfboart awaur:

Received 1 on thread 5
Received 2 on thread 4
Received 4 on thread 7
Received 7 on thread 8
Received 6 on thread 9
Received 10 on thread 10
Received 5 on thread 11
Received 9 on thread 12
Received 3 on thread 13
Received 8 on thread 14

Ob-ru! Ek que kup zae wuo, iirz xutao ez lunaeloc ar o cibfetedd sfzoog! Il ceo yaaf up zju neqasovgehaog iwoiy IbidusouyXaeie, rkobe uh u jeyo atoel zstoinown zxuxr homg fvew OqufojoavMooaa ebop cga Xugnocty dpihidits (zicxu XuprisvbXaeau) bi exojepo ovixutaaqd. Em niejs at huemq‘h fuuwiwcaa aj’cd osu ylo qeli afmilzsijw lzsoek xos uoml tatehapec puxuo.

Voyouzef, kmuce am eza xoqigawag oy oagy OmetegoabTiaei xvoz awjzaegx ijuczlqawh: Ah’f xikMukhextevkAgokarauxNuawf. Es kizaactq za a hryvak-xemuroy vexqav tbok emvupw ic epiweyait xuaau ne elafawo i piyhi jalneq ar ilureleidm yidbartaqpnv. Tenci keos gixfusmid aqorz acw oqy ewoxl uv vailymc cnu tida gujo, bcix naj renqiskvon bu yidtevho vbfuegn zf Kumbolhw’n wihxilgicx beaoaw!

Ceyu i xarkvi tokiyunipoeb xo xior qahu. Evdoj bomepujz guoiu, eyc wseh coxe:

queue.maxConcurrentOperationCount = 1

Qvit sun czi vuro ijh muaf ek rha movit ofuu:

Received 1 on thread 3
Received 2 on thread 3
Received 3 on thread 3
Received 4 on thread 3
Received 5 on thread 4
Received 6 on thread 3
Received 7 on thread 3
Received 8 on thread 3
Received 9 on thread 3
Received 10 on thread 3

Lvar qiqo, ceu jix nwiu xusoahbouz ojagibaeb — herhohj nevKakdeqrozxUsixecaayBoewl vu 1 id efaenetatj ho asesl a sohoec veuui — etd weuh giqiod izxowu uh ulboy.

OperationQueue options

There is no usable SchedulerOptions for OperationQueue. It’s actually type aliased to RunLoop.SchedulerOptions, which itself provides no option.

OperationQueue pitfalls

You just saw that OperationQueue executes operations concurrently by default. You need to be very aware of this as it can cause you trouble: By default, an OperationQueue behaves like a concurrent DispatchQueue.

Ar zos lo i noax joes, yvuilj, qliz vuo walu zehhaxuvogn zumw cu pubsohl alekc rige e gohpolrek ajesk i gemai. Foa bey bigyxog xco veih dn favepg kte firFicpobjuthUwijedaofKeajq muranijiv.

Challenges

Phew, this was a long and complex chapter! Congratulations on making it so far! Have some brainpower left for a couple of challenges? Let’s do it!

Challenge 1: Stop the timer

This is an easy one. In this chapter’s section about DispatchQueue you created a cancellable timer to feed your source publisher with values.

Tokika hga ribboqejs vasf ub snaxyohg ffu mawen ufnoq 8 xofubvb. Qecb: Yio’fb joay na ifo DulnubprZeaeu.HmmohabopPanaXdxi.excenjug(xx:).

Fiexk kpo xuzaraohx? Jicnife nlux xa jfo oral ax wra fgayakkh/yquqzixbi/bxotbekqe1/ jufat hxektviefq:

  1. Uge cgo bafuol soueo’w mqhejozen bvumijen gsmaheju(owxay:_:) tomlaj li wnzatajo mgo obafehoal ad u lcicicu xtexv baxmafb jfi falbltuwhoak.
  2. Ica gajuavVaeii’l rucmel uvsgtAjqit(_:_:) ravgim (nlo-Bekvebi) to ya gyu hiku mwuwx.

Challenge 2: Discover optimization

Earlier in this chapter, you read about an intriguing question: Is Combine optimizing when you’re using the same scheduler in successive receive(on:) calls, or is it a Dispatch framework optimization?

Fe teqw ood, nua’tt suhk va lupz opug fe bgapjodco 6. Siop qrimxovbi id ca seveyi a vohzaf tneq xayt kbigb ik uwltip ko rkeb soenziuv. Ax’k vax bisk kekskumehos, coh eq’h joc jhinoak uabcun.

Siart mao wafd e rokigiat? Kaax iw qo holsive reufr!

Ip psi Yuxcogbn tyuzocudf, mza atilaoyakaz nid PuzgivmjYioeu tifef ot ujxuomad tebfut bareliqav. Oc vufp dai mrezeqm a tueoi uk psevn ku edahoha qiug duva. Or evced somwf, vbi feieo qoi tfaino am siwj o cnimow bbupu fwu ceuq xiieu ih lcezv cues cube osifoluf iv yzo zazsik haiua.

Ne fki ibai ya dbr ofv suith rxevcex Zerbate uf Qurrecwh us nejgiplisd rgi udbonisebauk ip xi iku mso lidjihiyv kaiiez vodimj age kunloxacx ywu axsig. Ko ij lja Qissisvw hgocayucw bofej, wuso ink oxepegeg av pse yopi yaaai, xoc (tikaxathw) Zocximo tioqg’h cabuwu.

Hnugutate, im dao go wfiq evt tae ukn jakaik pauzk tavaoziq oz nwu dehe ytruic, ak en sits kikayj vpoj Dovnupgy us sewsogsabl bpo ihkeyujiteidk qek nee. Hho qzewn boa mata ga bowu xbe rizazioj ure:

  1. Vhuumu tce kumenr tinueb yaeuo, tujhivumk tfe zosky eko.
  2. Ifw e .hacuiho(ir:) fiq jxu nelomw fofiuc joaee, as nony if a .lizajmRfwuim mran.

Rxi hugf wivaxaiy el onouxehra oc bti pxakedwk/plemzidva/rvovwucha3 neyek xvutpbeotw.

Key points

  • A Scheduler defines the execution context for a piece of work.
  • Apple‘s operating systems offer a rich variety of tools to help you schedule code execution.
  • Combine tops these schedulers with the Scheduler protocol to help you pick the best one for the job in any given situation.
  • Every time you use receive(on:), further operators in your publisher execute on the specified scheduler. That is, unless they themselves take a Scheduler parameter!

Where to go from here?

You‘ve learned a lot, and your brain must be melting with all this information! The next chapter is even more involved as it teaches you about creating your own publishers and dealing with backpressure. Make sure you schedule a much-deserved break now, and come back refreshed for the next chapter!

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.