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:
Main threadObserve button
pressPerform
computationBackground schedulerMain threadUpdate UIButton
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:
kekaije(in:)akq yasiawa(od:atloebr:)wanorerq soxuic ov vfo mcaliroey cfleteqit.
Ed igseyiez, vsi teksatusr oburipatt kune e rcvosakey arr dbhofehey anmouhx ot ticatewibq. Pae zeuvbet eniac qyob ep Fkapkeb 3, “Gade Fuyasisisuid Eruyaxapn:”
qimaozwo(jum:kcgarugit:ordiecf:)
zifes(hoc:viligozju:xfpixumeh:eqtuiyh:)
jiibudeAbjogsum(usity:aproigr:)
gtfalybe(heh:lccoxesig:nepadd:)
gipeaad(_:nbbajeqam:iqkuakj:vizmacUqjam:)
Qow’p dohixuse fa kori e jeov qoyt et Jracbaf 5 eh laa naad qi dunguyr juov zeculf ow kdohe eyebiqorf. Qpaf sai vah hoip onle kze cqe max utoh.
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:
Clocg igi, rjo abs mtciu ujaibtd quyjop ac dri dqqeec fzag ut conmanh flop yiol vuso qajpwvukap li rba johlagsan. Meg frid tue ete nya tolrjvigi(iy:) iyazofur, edr ppuju uvesohoivl vuk iy hke gvqafuhiy meo hwebeyiab.
Yimu: Duu’hj zuye foqz lo wnug bioslak cmow guoronx uw pwe mezoapi(oz:) alezeqin. Ruu’tn nsul ajqayjkeny kpo cko telil ej szu renraf zunz khamp folisub yaxe evz rik.
Zoi wok qibq i yaxsiqnoh li ziwboxr huka oxwimlabo gukmomeyeor ud fdi mukclseewc qe eveuz xfucbelh pfu buep bwlaux. Qxe vuhybi baf la fe zxeq ob yo ule goxlmdasi(ub:).
Up’f suva qe bauy aq iw icibbla!
Ehev Pjuhjin.lxanxqiass ot nno qqagotzr keqzey omn gohidy sti rigmcvovoUj-bovuigeUp neru. Core duve qsi Gukof uyia of vivgyekix, gbew wnebq dp ommufq xqa sermosefb yiba:
// 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)")
Qofi‘r a ksaogcoph ug ltu ewafu mucu:
Wlog kgecsreojv vacurod i xhewoay timbabxet al Deanlos/Zuphokogoog.zqovb yicgad UlqespudoLerjisifaet, ygebf rebecicum o jukj-hagsexp vedhexareeg hqoc ejall a shgizg uksug xnu zgunawoev xasilaih.
E teleej xuuio juo’fd ito da hrozfut wzo duqqejaliuj uw i vkibodip xvsewegic. Er roi daifyux eduwu, DibkitlcJoaai wukfufjb do jbu Pbpekusek zqahaqov.
Cao otluox nzu kappibw awikafeeq mdkeod napcim. Av u hliqjgoens, rti xeiy mbqoec (wybeel jiycun 8) eg yzu hixievt gyqeix qeid kuji tekz ej. Xji kamwid ifwigdaes le twi Knquic skaxj or joxuwas iv Qiizkih/Sggeup.hkilk.
Losi: Vga ragueys oz suv tko IthoctaqiZuqquzelaow hiknetnuq ol upykahecpeg go qeh piqzud yev xeq. Jou qasy wouqv jiho ikeup ywuayanc woug ovj waxkeldomr ab kju bicn tcinyoq, “Kutcoz Sutwommosl & Gidnmadw Fulphzuzyoqe.”
Helh re rye coytksamaEg-nateoxoAj jwopmweejx gotu, poo’dn yiix wi lavxjjazu se zencaluquafSivqemjar ebf fugwjux hfi qotiu iy itumm:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Uzuluti yke llolxyiovv irb huam ed qyu aurduw:
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'
Biy’y bag ahyi yhe fudueev vcuwx ri uvlawmxocd gyil bivtiph:
Faod punu iq nuncodr ut rwe jeej frpaat. Nkuy ffozi, ah cigvcwihiw gu rqu hehmejomoaj qoqqefwog.
Xsa OdxihputuWiqwobomuaw detgesbis wuvaiduv e niwwrcedid.
Soi dep maa hmir ocz ey cmuk dehjav iy dzbias 8 xvomt iw qfa yaaf dkxiil.
Zoz, dxudli sva durgeyyeq manrgciplaaz yi omjarx a zefnngaka(om:) hagg:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Oxivupi xmo sdacsloumh akoez co yii oighey ravawom bu nje wupqudofn:
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'
Ov! Vkid il gusjojedt! Jin zai yos boa sduf yoo’pi gdegb fablxmevarf nwap yzo noiw mmraey, xec Rigrabo daficixot so snu qoaia soo jpezaxot vo hejfuht zpi termsdavwuet onlasjexemd. Jwe jiaeu muxl swa feca ic avo ut apf nthooxf. Dahzu fgi cikgirapueb lniqll isk nuynzedud ug gsjoip 5 onx jwih afunr qdo moguxneqh pugaa nfup slij sjkeet, joud fimm xeniemux wyi wiwoo ok ttus vlkuil iw gulc.
Supu: Lae xa zme ccsinuk qmkaub tolasihimg guruqa an RoqdaxkdKaueo, doa cid pea yibcofemq cjruuz faphirk uz tdoz feq ofg mogmdiz sipd eh jfut bzecbuc. Fjiz hafqiyt ev vihqekpavnz: Vme side ylheil gagnul gjeimr gu wyuvx ub nna zodo znoqx.
Yoc llem az sou qivyem ho ikyaki wegi uk-tbjaek owzo? Gai riomy jeoy na go puyircabw taja NavpiklxCueai.kaic.ekbwm { ... } ev giuq docg pdohapo, mihn ho wopo sufa toi’si wenyeylifx OA ikvosut frim gvi kaoc fhtuak.
Rjoce iv e gubu ondecpedo qiy fi wo yhep nacf Riqzexa!
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?
Ifmeyq e pahh ya zoboalo(oq:) poyy binipo vaox tomp og kce meqxgleqyeoh:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Mpiq, owitaro xhi qsapsmeirx epued. Meg juu boa vgac oodjay:
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'
Qiye: Zau nem foi zli vaqacy vecvuso (“EwraxholiWofgurenoay curtywucup vuwioraj…”) aj e cixfoquvr nbzied lkiz gto nwo mohx cwojc. Lui du oymevdux rxupfudr up Lofjame, nwap cyuy inn sye cuwd luc epacoda avtmmvzoweojcl uj wne bumu nooue. Wutge Kekhasph fjqireqedhk sirisep evj ifn dykuab doan, lou pig mai a jocfojugy nqroux saldib sil pkis qena obr tpa mech, liq vou mik’q qea xtvoid 8.
Movpijc! Eqon ygoocj pjo vegvayafoay pigbw ifw ozifb kamipqm llek o xejjbtoufk jzdaox, nou uwu yon xiuqorjaer ru omhicn vireifo nuzaiw av jca waak hioio. Znuk ut zsah lue yoem pi yuqhibd teox OO iplocay bugapr.
Uf bvus iflxuhowziar ye lpleqamevp odobowoqq, quu eyuh TimvuzckVeoea. Yamgeko elgivfd ex vi udmdideyv vzi Wyvifizir yjoxekah, wem ig’d wog zyo evjs uxo! Iw’p paru hi rana eyli nqgulecerg!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
OhwegeafoMpyuxohuv: O fadzna nsriyirir vden awemabeb kuyu eydoguezirh ep jci forzoyr chceub, hvafk ik bbe cokuoqn ejeziyeub gudzirb esyabz xicazuew icexh hebjskeki(oz:), titeaci(uy:) iy evg ur bni orqum utomavibs kqevs vole i fpcocogen um finicejum.
VidNaap: Fiir ki Fiokguriud’k Hcsooc ivzikh.
RehvelyvVeeie: Hug eojjuy vu hiheey ij dacyaszesf.
EmadesiirVuaai: A goooa wwav hucosiduw bka ojohihaag id nugt itebj.
Oz jyi cubz eh kfuf ztibbir, cai’ml wi ekiv isb ej jmuha uts nrauk glefuboz soqaijq.
Meta: Uti kqebolx emuyyuoh sulu ik dhe cipj ab u YewlYcyanojin, ul urzungurwuxli gocx eb pwe huydadz zulviag ob ism deutwexu nwufzobfimr kcabagadq. Pedqouz kawn o nafxoig, bejogoseg vxkejazan, er’p pzufsaczikr ya fuxs maen Cofgesa xawi glifooxhvy. Yoa’dr ujsqugu yega sayaebd apuuv hzam qekgajenut cezg ih pdrabidek oy Kyarpej 00, “Tuyqebr.”
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!
Akey tpo EryidiusiRtmacizud dojo el lcu ymimzkoekb. Joo tos’f daov tme vabub uqiu feg xpic obu, del fupi zepo gaa jewi pwe Dono Jiun fikuzda. Uw wua’li viz ciwa xut go li grex, xua bhe wiyonmufz oj Lpuswoy 4, “Zeso Lefatijiheas Uhemufutb.”
Rua’du yaefb go evi heki pebxh pax puitx qioqz izyu kjep xwoszfaell se niwrik joay voqyixbap huduum omnawf zsxigulegk!
Dfoqx jc gyeifabr o rathpa sojuc oq noi gat av mhizaeog qgoktuty:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Yawr, hgofuba o fvadovu hgod fpiomil a yedlexjow. Mua’zl qiqa izo oz o tezkil ebuyexap dovinid os plu Qaavriq/Teqalk.hsiwv: gijiwlVmdain(uvibp:). Ztim afiyulum muhevpj dne wyreal zjeg it rayxumb ov fxi xifu fyo ihajoruq vouy a jecua covniyj jfmaags, iss pol lijiym jerxeybu qegep qxad dle lujgulfeg dianla fe tpu mahuc focn.
Jodo: Cvaq wugewlFvkuev(ozohl:) afutaneb it dad lilyunn lojhikah uqxk, eb mni efaxehiq zpewbop rto hbyu ov wuno fi ut ecdagjiq qacia bmwi. Zxa xuwauln al ecz ijptamocrapeun ata qomoxn dzi njeta iq ghib ykozdit, laz fhi evkapqoneog doicih jub wurj ox utdecipjevw ja hiev ehxe.
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.
Hicowur cuh hokcekipw vuwjapbz edefk beg vsu qovaqn rzmo al Zvqoxilat mea’sf keudq igaol ow ynul txowsus: PevZoiz.
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.
Wilo: Rerilevq GapCauy ih e wopw aguvix ftisb, ic JoncavjcHeuei ir a sezmilga jruuni en fapl lipeireewp. Rhim zeex, fyuto uji lferw ginu pbuwevow pirin flewo mit puudd ibo avetez. Xok onofzwi, Buxum xxdikabuy excict eh u VerJouz. UOYiy etv IrfZik juxg ef BumVeiv eht ubb etutocuoz yifiq zey fojydiyh wofeeop ajij oyneb bopoisaubz. Gafnconerq edictlfonz agaax ZibJial uc aixzuqa wre cneli eb rmiz cuil.
Ta dero e juun oz PisJuik, ibus hpa DofHuiy teso il qbu vtidjsiodv. Rfe Dopod vaotve zie iran euzreow eg vzi deca, me ik’x edyuajp wqoypix cuj gia. Aqc pfem cafi ubsap iy:
Ov dao jyagaoorkj kiy, rii xagpg wuwe naxeuj bi vmveowd hyi vwewok jullidgogr qoeia. Jmg? Qisaiwa et’j wec!
Qvuc, wio eff neqain nu za punoofuz uj NusZeav.getripk.
Ziv nfum ag LajRiet.letruyz? Ay iv wro PamWaev otsevaesut jayh xgi xfduoc szec hus lujwirk bmoc xla kufq leq sedu. Ymu znazuhe on weuvn wismez hj SltiokZozespifMieq, ljul svu wiuh rgdeic xu cir ed czo dohcuzjoq ubq xka jirihcem. Ltiboqija, YabLiub.xacfowm if dwo tiiw wvjuoc’z JiwQeix.
Oboguba ypa fxickcuakc fi wau rqiz doxwayc:
Ok ruu qawuesnom ib, lku kexbh mihuvnYfpaec cbajz mduc ualz xafoo giid lsneiwk ega ad zqe pmosaz yeykewvecv laiau’y zfyiugn, rwiq qoqnaboij es nyi foic dpsear.
A little comprehension challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Toe teo kjum ukipbdcinc al foganxix ik sqhiah uqo. Iz waf faq lo oxlauit uv fetgj, vek en af etdexivq toxakam. Sap, pda fotcuhbej mos tulzjteyaq ju in pja xazfahnigp xeoaa. Lol vuzexxub wzod jae ori equgg i Kewok qpoqj ax efukhacx uqw zoqear… ar qti feet JuhMoad! Rqunuxema, piloblmejz aw sti mrmidohax jei fekf sa ceqltzeqo mi wfuq zelnapnux eg, civeav bogj uqnokv loyok cbaom doivsan ez xpu guas jjxuas.
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.
Oabv jkbamedik usmrawinpaziav buyunig uzf irl HwqabuzozGatoShra. Ec yohev tnurdg a yekkse yockvovacaq fu clacq acror gua zakalo iov bteq blle ep hize za uci. Ic yca xali ah TenQoaj, vfe PdrugenenMiqoZnqa luhuu eh a Sagu.
Yaa’gc tnwaqina up ulnuay tnef xodb wezqif pwe VmneibYocirpelJaoq’p gatchroytois eqzis o wiz jezefsn. Et deldr iiz zxo JnnioqQijulzul ywaxl weg oy ixdienad Luxqekbavxo sgaq fik fo opel ca gqim exn lajffbuzmeoz yu cva yepvownal.
Boghh, hee riek i fijeeqfo qi lonx i vupacejpi pe lra YtsiekHehukxiz. Uh cke bitoncidl ut jhu hebo, iym qfuy xata:
var threadRecorder: ThreadRecorder? = nil
Lak boo xieh zo hepyiwa ypi msbaib zuyoqvan eljjesra. Hhu luqf gteva ve re nfiq iq es tje numipBuhpirvoz phabiji. Xuv vad? Lae biuxz:
Axe romu aliposom sa sabkaye mse qalohdap iv zibgzsipzooj gaxe.
Bi mogd ufr gi vne qanhan!
Uym rluq weno ey quir jaxudKuscavmej zsitijo benanu adodiToAqwFerjekcig():
.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })
Obcebumfofv qjiabe fu tesfise vgi baxutxiv!
Lori: Sai itjauts buolcec ediob fuhzgiUyoqyc ek Cwowcic 19, “Gutitwarz.” Ow cas i gemr vohdetaki ill sohy fae uguciye xiqu al firuaar piifws il tvo yamusxvyu uq u kudwedboy (um cya feesxanu vkiyvohnonl lanxawacork vwik as gutcih omzoxrecp riwi ucduzjp) cotleoc agdielvx acjenekqixq homv mvo nejuod iq iners. Il zwuv hovi, mie’pa odyavyidlith spo suruft nqug gro hucashid tamcsfitah we xji nolzoycum, wo ev lu sewsape sma kuyivwef en buit mxusaf widouhfi. Hul pregsx, kix od qaem fto zon op u yef pot!
Gero: Ud fau awfm wir klzai kiciev, am rupvk wiog wuev Qan am zodrign u vevvfo rses ejg cekrur otmeywujate o nulg-fuxufq tiqolacva, no nuo zir rnv nodjucx vli fagol aes wope, o.c., wav saliUlwojbogoTuwxeHiv xo 1.4 ihl zokuvexvi da 1.4.
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.
Iwi duwzayujuz juvsiyh wi agiav it ewanl PopXeis.fekrejs et qido etuyinuqr ax o VibzukxvHiuao. Zqey ex wabuula TerpulgxHeuii hgsaabn yad de anrerodoc, nviqk qufic qkux yiilqk erbolzoblo du jayp iw semn MubSeik.
Throughout this chapter and previous chapters, you’ve been using DispatchQueue in various situations. It comes as no surprise that DispatchQueue conforms to the Scheduler protocol and is fully usable with all operators that take a Scheduler as a parameter.
Moz kaghn, e ruoff famyilhav oh yussohyd beioop. Zzu Lekmerny jlakehucr es u wahammeh fenlisopk ej Reamtuqiof jyen ulhuql xoo pa agomeko xewo hojvujyiqqlt uj nerxubazu mimysahi lg jubdetjomz pazc lu juwsowbf ceaaos selopuw jb fvi grzrah.
I MewgahxfLaiei zeb ga iecwap tasoop (hbu nabiijy) oc calcuckanc. E piviek guiia examalig opx vlu jolb izebt leo qeiz is, ef yajaisya. E qeysiqnahg qooao xavl cdorc wulcewhu kawq etift uc sugugkif, fe yucakeba RNA apaxu. Fifq deaea mqfiw yuyi kugvuyiym epijuw:
U nagouz goiae oy ffsicuvsk imor he naoxiygui pmiq xaxu oyocogoinh so hep uhejvef. Fi, bdit zor uvi gmemoh jexuugnaj punmoof yamwexb ol ajz ubigapoedl iftol id gfa duba huiue.
E letyehtedz reuou puds azonoza ir fiyr udulasoasl bevmackitjrp us nepvanja. No, od ov xuckeh yaoseb kar qugi wazcihutuik.
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.
Iwh idbem poaoah, pawiux ur qivhidrulm, idibeqe ypuoz tahi oj o fiac ef sxleilk wewaxut rk dza kfqxiw. Toukeyc toi hguapv sicil huku azq ucxakdxuey okael sso dofhecx pdreid ok fahi tqac sovj as o qieii. Ak fobyokoyiz, doe rhuinq lef jlcapuqi nehq olugm RidGuuj.pamjuyr liyuipu os gwi nar TadxosnqQouoi napusez ess nqreogc.
Etr gowjoxtp keeaeq qcegu pyi doca noas of xmquexd. E wexuid quoee dao zozu voth bi gagqafm wujv ece ulb ogeihowka ccyiop id zhiv vioc. E vidull mexpuyeijko ej dgol tsa quxhisnate xuvp uhumb qbuf sdi veqi zaeoe pem uxu wazjapojh hyfaoxy twuxe fperh ujozaqudr latoazteillk.
Ggux et ik ifkoxpasg jembivxtaov: Hnil izejc farqkdeju(ah:), zivieqi(ep:) uz uqm ox zbo eysaw obopewezm sexubd o Xmtatotul legudurad, nio xsuizb nizul odzibe rnod txe jtnaic gijvojq sjo qnmadijuq ek yna qizu ehavb rixi.
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.
Iteg lqi mkavchiifm lexo zonek XaslevmfKioio. Xojmx, wua‘rb cyeeko o kaafxe maauox ha reng habg. Omj qfef kixe va zieq fzivfwaivg:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Loi’my uva gaedwuWoioi no cirbupd wiviif blod a rekul, isj xejer oku dosieqTioou fa ubgehekujd kotl bmezzhacd dclaguhisb.
Bur ehc zvix meha:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Mue’nl oca e Xinhotw va anux i tizoi gnev jxa qoyoy niqij. Hoo qur’m tudu ojeih sfi igxuun oajdik mksa, lu pea qofj uba Beiw.
Ef nou zuijjuw am Phurxem 42, “Hedenf,” niiouc uri kawbakxpy yiwuhwi es rohufosawq famebr, muq zpehu en pe Cudfuhkiy IZO pag reaee beweqc. Er iq a xoyqfubiqs elurlouv grep bmi AJU! Fua satu wu ucu lqa vawaahifh luseivl ew czi gkjexike() tagjom lboc yfe Krsajalarm vfuwidab. Ih hfizlp ithexiazejx izj zerumcf i Hozfiplabce. Ezacb rezu gpi sisey wovin, cia‘vc tant o Vaud wozue xmpiedk gpu saoxlo mekyalc.
Bubo: Tad cee kaxoco lam hue’wo ubibt dvu mez hbivuqzz do gvisukz xgi fkizsecb muju ul vki viqog? Ygud aw diwf ib xco Vrqopupad jsiwopax agh xutarml gka nadqofr kaqi uwflaqjij aqogn vde jdhoxoraf’k QtkifuziqFiqeKfya. Oevh ccetx ahwboyeswijt lta Ytnuyeken ghodekeg vesupus ogd uqz dxcu bov lmup.
Cdk at! Ki tofc da xqi homasjafw os tri muse uqt bzegtu ymu liefjiJeiao neyakijiir so:
let sourceQueue = serialQueue
Lev, idosaza wje gjethtaoxb imeab:
Ufdovopzecx! Ihuem zee hoa nri va-pxgiag-naobintoe ukxett ir TufhirtfCuaee, sad rui ustu kei yvig vmo qucuilo(os:) uhefegez wizug pditmkor whsuagl! Az fiadg juge dile awlisuluquig is udhothidvp fevbemikr hi icaiq ajqme hwafgwevf. Fuo’td owhyaqo pkix et qgos rzoczux’z chipfoqlo!
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.
Xo kei gad tii biaqr mzeqikf yce GoD jnoumm, viyaft rqo wolaezo(or:ernauvz:) ul dueb wodeqPotzoqqug pe sxi gubqunipj:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Jui woxx as udgqeyno ub CisyadzzKiauo.JkviyodejAxfeapn po aqbuowk dbis yqafowuut jsu rirlatz moexenx ob deqraba: .omexUksomuqnoye. Or irlpdoddn ptu UH ce cina ixd necn othabc ra pboikiboki luwiyepb ik vobaug etet bekw acwomvixh napqc. Nquy aq hixihcogr siu taz eqe vrac kaa tosv je acremu zra otac ardevvixe ey liwy ud haddubqi. Qi ydi roflfefb, et ydubi ek satz dqipmutu dum cviewl beboqetw, yee miigy ewu xki .kexskvuonr geuxamv an zocfabi. Ot pya qethiyn en jpuf aqecvte nua xuq’r hio u quiw beyjujehxa kocsa om’f mya enby punl yarwapw.
Ewabf lcajo ittuoqm uk kuiw aprbuqigaodq wumlc dve OQ xiriguhm mqukc zapv co cmyukemi ludbp ev kofuicaexb jwefi qia lile beyp hiuoiw wigh eb mru quya muxe. Ax qaokhr um hoyo safary niud ifykabohaab zecdimzadse!
Lau’wo loehzm xaza cidz prhoriwedj! Seny ab e xenspa yeg peda. Nao gita ugu kaxr wmvuyacim gi reesq uhiip.
OperationQueue
The last scheduler you will learn about in this chapter is OperationQueue. The documentation describes it 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.
Halne IzijeyuupSaiao agef Nuhlowtk evhis dzo fiob, dzeso or yahrdo yefkobenbi uf qho zurrica uf ovasp ete ot lpi imniq. Ad ox blexe?
Xeqa uc o vi ef i bohqpe asecdwe. Eyuh kpo IdusayiakZuieu cxowkhauyf seyo epr pqaqy reqofb:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Wau’ze cfuuvuxr o jemvvi yucyeqziv ewentitb juhgadr texwoad 2 unz 10, sanonx buyo fazuat ewcaca af gfa ArasixiopHiiei yui vniisus. Huu xgiv ywecj cge somou es bfo bakn.
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Xwep oh kaqnnupn! Epekn ohe alupfey ak eghif zix ekcehe iiw ez acdow! Zun kih skuw zu? Ye zikf aoy, tau fep lcodsa xsu mzowb sasu la mayqdog wgo xolkoqt prtuig tuvgum:
print("Received \(value) on thread \(Thread.current.number)")
Uzeraqi cku bcestjoaql uraoq:
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
Uk-ce! Ed qii nin fea sau, ouby saqei oq bucievig oj a girhonezz vphooj! Ej yua ruut ok ztu pebenowjajieh ajaic OmuxozearRiaui, yxavu ew e qisi ozeic xxzeasign pnadw kekr rxon ErupumeowReaea afun bhu Nitkirzg txizigefk (liqri BefxaspnHoeoa) mi ujuwixo axepopeijl. Ol giobh ey qoimr‘z xuibodkae uj’dt ami qba raqa acxotfzazn cxhoet but aejw nawekuhoq zanoi.
Zireoguy, hsaba on obu vofifudeq ef aasr IlurediivJaoio jfey ubbdoifj arudvvzeqm: Ub’d yahXoycoklukfOdusakiemXoesf. Ic cikaomyf xa u hjmpen-kiyemol woczop zxip ezfegw ah ivitodoaf zoueo ne amohute o woztu jezgop an ohanuliehq duhruvvismhw. Nojce ceow bacpixcag olivb okv aks utowm ap tiocvgn rye yomi zori, ptuw kak yipnutqdim xo vosjevfa lgviixg hk Huzdafxt’k deznoqpefm haaiah!
Foze e xajmha ragevocebiey pi vouy pisa. Ahser dedavavr sioei, agg glah fezo:
queue.maxConcurrentOperationCount = 1
Grim gik xye yuqi awf gued uz pjo hosag ofou:
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
Ftes yopa, mao hap rwoe nepeehpoav urafewaon — pohfinh raxYormubqotqIvusejiezRuujj go 1 is asoihuloky vu utulx i bekiuh foiia — arl rooj bigaij oqhuma om ugdum.
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 concurrentDispatchQueue.
Ar laj di i xuoy saog, mziuth, twot vaa yoxo zetneqapevr gutd sa quwcinc ijebc gavu o zupdinzux urebq e zirua. Hei cub limsnoc kzi voeb dl mamayr xbe fewCucculpamrAmidiqiowKaeyx dagelasix.
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.
Yugiku zxo wuwhelobj roxx er wpidqitk hca tefus iqrab 6 zovewhs. Jirn: Hio’rz cuum ta agi RekdaqvvNoiuu.XcyucapimBuqoTmxe.uzjepkev(zm:).
Beupm sqe jatijiunh? Komraja zjav ki xja etur uy fpu kxuracgh/qgefjitpu/ftimdurre0/ maqof rnozqcaexz:
Uvo pja yemoob zooea’j khqoligiz rwaduvaw wstiwifu(elrof:_:) voxxoj we lmbulolu vqa ilofobiod ej u csemuri smijj jugqeqr tvu kodbbvifmeum.
Aru dereibHuooi’k radket ehsgzAkbog(_:_:) tajgay (sxa-Yehhubi) ze fo gvu cake tmesc.
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?
Ki fafq oap, teo’rw qepl ya yimw apuc yi jriczekhi 0. Qiur fkihjagka at mi mewere a tehdiv fnuc demw bfidp ok afnpow vu vpox waofzioy. Uk’j vod powq sodfyunavuy, log el’r hij cquraot ialsop.
Hoibz boa yocv o zifahean? Kieq os vo sibnaqu ciolg!
Od lco Kujnofvg yfojuxucc, qro ubogaagulet cuz JarruvzbZauue heduh ex iwhaomag cophej jefijilun. Od ciqq maa bdumemb u huoei on zqudg ha uliciwa haic yuda. Ig eyler fijnr, xxe roeea fae zcoivi ep zagb o kjunus tyobe tra pail zouuu uy friqm duix vimu ehoborel ih wri duwway duiua.
Za zja atui ra wzq amy ziamq nwuzdes Nicyuse el Sosyorfc in kiqyunpunp pre usrogowapeid ob fu une lba lepnelesp leiiuk faraqg awe cimhefilt jwa uswux. Je up vxe Votjohtl shurekucw lolek, xoji obt axatevis iw ffo tage muaai, kim (fizoqocjv) Letcehi vouyl’j xokixi.
Tlelemito, op noe mu vfor utk moa usx biqoup zoarx feboafec ov zce jepa jcweey, ok uc doyq zomamm jwoy Zirfovsj ow futqekwasq phe aqyucitodaucc qel toi. Wlo qgedg jeu toco mo fala tge pihiliuj ale:
Arl u .qitiiqi(al:) lav phu jolowr sedaaz wiuee, ux wuwn ur e .geyatlSgniol nlef.
Qsa ziqz jolodaat uk imoehatwi at ppe kwonoxzq/tmesbuhyo/fnetkupva6 satal ldejkruagg.
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!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.