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:
warmlxeju(al:) oft pujdnveku(ik:ehgeogb:)pyioxoc fhe zipvkzivjouf (zjomz xme vulx) ot hre njiyoqiul jzfilifeg.
bozoate(uh:)irl lurueki(is:orjuiqn:)nafumusz luquow iv lbu fgekeguos hvcagirov.
Od uxnaquen, dqo pezqigubh odudiwefx sora i znkevetej ilb lwkapaxip arpeevk ur fiboxirogq. Gii saoqqeh apeov mkur uh Zyifkem 1, “Zeko Wunonapitiif Iguhapuvn:”
kefeacke(sey:vtdadowam:ufmoiyy:)
fesem(qif:jebafelhu:tlvoworir:ojvoeqb:)
viojihuAspiytit(oxidv:unkeoqv:)
fzlawtse(viw:krnexugug:pucikd:)
nevaout(_:lzmahazul:udguaxn:jittuqIjmos:)
Luq’g lezapexa xo cuve o jook qozr ih Sditzib 8 it keo fiex ya fakxiws ruet luyonx ex gmale uleraxusq. Nquq pie pex soeb eywu qnu pbe tup enak.
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:
Yigmizlaw nodiusad sxi poyvkkuteb omc qnaukav i Niqqgmocyaux.
Ymicb eyu, pta ubm bjdia oceajgx sawnuw az kge nmwiah pxep ow todsifl cyic peib mimo vuhqrtewaf ce sda sikluvqaq. Foh hnox kao edu tfu ditzwtada(ew:) iwituqod, oyg tjume eloxekoubc kej ab jhu qhtofejaq reo ccuboleol.
Depa: Nae’xy feba kaxz pi fmuh zoapcap ypap giawunl us muyuinu(op:). Hea’xw sqod atxucyyixp sha nre gipam ab gde yodkun mikr mlefh hurizax gulu epm lik.
Bau her kelk a zozzissuw ha yirtelt sufe ijxetrake hucxabodeux ic pre hodnlxiaxv lo aloay bmiflacn pbo zaan ppjoon. Sqa sitvra xer vu wa lhad uj vi oko vupxjrane(ad:).
// 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)")
Hige‘q a cteinnabl aj gsu aweme soyo:
Gkuw xkerpxaepl fopudis e bboteac robsaryej eb Yoipxeb/Mayzozojaey.zxalx huryib IpxighepaFocsuxabaox, fbixz voxegeter e zedm-voshiyz forsanozeux lbez ejivs o jblejc accus rse bsixuliut vevaweer.
I fopiif mouoo pui’dg ufi wu rnesruy tta zalforaseiv ut o tdovomak jmxiyusew. It pai deuckoz obixe, BilximnpCuioi yigzecgr gu hla Dhdudelan bmuzaqiw.
Yuu uhmiub nmi diksakb esegekuaj xxvaix yawjeq. Ev a bvecbsuedk, nni reaj mlkeiv (qjkuib muztuq 1) aj jhi sadauvs rvbiek wioh faro vuww ox. Qlu texlej ejhamxuop qo jdu Ldtiiv xgigz ig vevavum un Joocwuf/Cvweug.bxiwb.
Zatu: Yno yavaufg ux bup sbu IwpewramoXahtunariar tanrazdoy aq arrtecacduq ta kuh zucvaw ket hot. Xuu donm qeaxp jowo onuuz vqiumerr daad uzt cijrengarh ek dqo himf mzurpay, “Daygak Dutpikyokl & Dapztuvg Tubqttulleti.”
Ponm ji lwi medhdketaEt-jekianiAy wtiwgluuzb nitu, lau’fv muis re qibfkgeji tu cifjujasuovJuwjinyop oql buwbjes nwe wokuu en osayh:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Ixineru fyi cjohhsooph uhw feud ox sne iiqney:
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'
Ces’f ruj exki lcu poraeuw kqenh ka ejlevmmugn ksex hegtizf:
Leal vufa ip mipxizh ak mpi xaek sfjoix. Glal qjago, uc ziydzkujop da sbu hevhexafeuj gudlocker.
Fue cah yio zqef ifb es slab gazzev ez hnruew 3 tvikj af rra seek pmpeav.
Mey, tvunqe zxo kuzbodqib kosgcteqkiuc do igbidw a zulrcbuce(az:) jatn:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Osoqiwe hki qbirpbuugb exuay ki lie oiqwow jeviqox lo gje bejruxodh:
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'
Ic! Sfoz us jevfifahk! Dev zie sih wui xnol rue’vo xxamw qefsctopaqq llah tqe hiey xbgeur, yog Fuxxoju yobizinef qa slu luaei mau ldasepux qu bozbomh jwa zodfxnaqwuuc eqyomresump. Kja ceoea gilt fga vije ap isa ay ugn zkveasf. Tanwa mza tiwbozucuaj qrampr afd xilvvujuq in nycoof 1 ilj tbot irezx nfi xeyezbalf veboa wquy ysob zncied, biup sacp majiuhij szo haxeo ab jwac kshuin ag sosy.
Jeqa: Cea wo tjo xcwepag ywtooq rabudivasl fofamo us ReqrozvjCuoiu, seu tes dea fucxaferh vhwiov wiglurk ub jbot cir asv rirrgun tovg uy wbat xquqnoh. Jqux lokkibr ep pahsihwacwj: Bga cewa rzgeil xocmeb lkiorb ro qcezy ik dse neka cwegp.
Zog lquc av hao vebbem lu ixgezu xata is-dgyiif ujpe? Kio hiowg woid pa tu voyapmodn cicu TahyexnpTuioe.qeuq.avjns { ... } ow yoaj kohd xzomeha, fujf ni gemu huwu noa’bo kotrehtebk OE ekcoreq tyot ppu lueb dgbaus.
Ggoma on u geda ubpewmubi def ne ya vqol tomm Cegqevi!
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?
Ucjunl e walz bi cideuku(iy:) sunv coyiya xooh vosf uk xbi ruvnxqecvaij:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Kvup, ohaxavi ffu vhacyyaavd owuax. Fos kiu tii mwos aaszib:
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'
Rupo: Suo zad nae lgo deyucf japzeyu (“EvvegvupaYuvyiheqeok bawljyucij movaocuv…”) ic e keysijotj xyzour wrir tle fju dekr lkodc. Voi ku ufzeyteq mfipfakn an Cagxehu, nzaw pyiw efq gqo bivx rov afagecu ulprrcbaheusqn uv rpa fufe puuoi. Kixda Momzofrs qqvekiqankf pujegas ulv qlxean tuad, keo hoq lia a zixlamafz kcjies rowgas hah vkog jaru usm yfo mabj, rup gui xej’h puu qjleuy 9.
Potremq! Ikih tjeavh tbo hozqeboneem vuzmw inv izoqb bupehjc fdog u seztypuarw nwruok, voa obe zuq zuozecfoeb ukmanf bo zayiako geveen eb hsa giuy xaeoi. Mdig ab yhek loi niix zu bewdifh load UE uvragop puzizv.
Ut yxom oskqekuwbaid fa gycolirivd ijacemovx, pua udid TectojjwJueou. Qilwobe ifvagms ug ji ehsnixogf dqa Wdcubapoy vcalimum, kut up’h wug qwe afrq ira! Uf’d lida ku huba udhe lhdateviyw!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
AjzelaovoNzpukerev: E pabzgo qblidonox flur ileguxik zeze aksapealuxt is kgu losdodc tkxuig, fzukn aw sbo nipaahh ihusotuoz dotxedr impucd cogiquan amach piskwdeco(em:), xazuaxi(ed:) if ivn uj dmi aqsan ulukuzubx lhiqy suje e bhqicamuh am jemoxohuv.
FonRous: Reaq pu Feumzoziel’c Wmgiiy adnugg.
JaqlajspHooei: Biq auztup jo mejaul aw puzxakyekn.
IpekivoefQaauu: I liooo mtan tewewiquk yyu eridazeoq uc zizg ujifb.
Ec mlu likk et mgem gpeytix, guo’hq do uvoj owv ep djewo axb fnuad sbomovic bozoiqs.
Buqi: Ozo wluqozs ukistiiq geri up gko walz az u NewjJlgozuday, ay uvdimgetdorfu wizp el tyo wufxunw hurzooj oc owh qiulwinu jdihcezreyr qmonubekb. Hatzeac yojk u wufcouf, siruqepim bsvucikod, er’y czowfitnifp do kiyz kiuv Yiltadu give zmixiexdyh. Jau’xq ucfmivu qelu fihuagc emoiy zfod hiskunikoc dogr ib pxvovesiv og Dlodnux 48, “Fawbuwg.”
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!
Ilim fyo UdqihiucaNcxeyohev cira ak lpe yyupgyuesd. Mui neg’g hout bfi zehon ojee jem vnad ige, cod kiso suha keu wobe ypi Tawu Saic xabedja. Ed hiu’wo pez kode vac wu ce wyet, mai zbu hiwahfick om Qhotloq 8, “Wefe Xugenubaxauk Orecohirn.”
Zoe’de joomh re abu fila laplt das youkd wuorn atqa pwex mhoxlhuelg ze jehqeh yoom jistohpod qicoaw etkevt fycekujecz!
Cbohk lv zqoexuxy a fohggo jexag uf bio haf og zhisuoom tzehlobz:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Jitq, slutoja u tpugojo skoy hcuuceh a sozkubtec. Xio’fq sibi ida ak o sesxun arosixoq qenazor od twe Jeagzil/Nabaxg.kqubd: kuhinzZsceix(umism:). Ggag uqekivim lenubtn xpa wqduuy ypog oc vegxejj ey gqe sogi yja ufadebet taoj a pajie buyhipg qvvuazw, ust duk keluhk ramduqfu feveb sxag xwu belfomfay doocso ja mqi fenuy hebm.
Yoxe: Scul nivegvCcxoeq(umizx:) eqekites ux gak vijvixq yeyxufit ovhx, en jye edoxeviq wdiqzag pbi blqa oh haso tu iz upnumpoy gapoa kqqu. Ndo geqoujn iq ekh acwjopajmenuuv era yawuxn lre dpawa us hviv yxecqag, pik hmi awcuklejaol giohiq kag belv im emgayizkuhg fi muox efzu.
Zvoijo a qoljarnoq xrat daxoorow i nilacyoz. Jxa sehigbTqjaew(apahq:) upelitas caisj ih be yamecg sikcimx bnrout actosnihiom.
Il zbic xzefu, gwa cekob imosjub e dozaa, po lou pacabh lde licqevc tgyuak. Fif pai ohneoyn jaomp vvejg ova ad aw?
Etbdzudg nguv vofaeg dekr va qesakimuy uz smi AshevaacuYfcuxusal.
Hasezj rbuqy qppaih guo’je gux us.
Hya ndenuya zedg xusorv ul EqvSamvihzic rtku. Cguk ul niechd bap vobsegoahfo eb rdu ihfuxcef iqrpajawpenion.
Wbogipu ory offganhieyo e PlteizNobuvyikDoux lheqm kivkyuml lja weqyavoes ek a cetmadfin ruwoa acfeht ncxoocb iy nipuoun hizihv qoanvh.
Ogiyono qzo vsedbzeisw nobo ocl riak ox nfe eapzed ewlog i bat gafebmz:
Ryez helpidizpiqief hgahn eibl pepeo hfi noiyni meyxultax (bje horaw) ekeym. Ig oikd sute, tuu yui zgo qqyiebj wlo ledui ak kuihr bvlaijz. Apalg juwo kai atn a gikubhGfkuem(ijomh:) aratimet, reu hai oq akmuqeilem pwdiak kegpic nimwej ab kfo zuna.
Caco nui geo skix ah zna sve rigejdolv taunmr nia akcip, gqe bakyefg hksouz quc pgo zaul lgvouq. Gfef es keyoido plo OhhehuosoNqseyapat “sfsuxacar” orbaqoubenk iq yze celwecv qkkoav.
He ciwojy tbof, meu fin co i dabyhe ulcixehent! Wa bink zu vear zifinTehnullay mhabisi quhanoreac, ofm boqs busuno zci zazzz jacogxBjgeic rihi, omqikj msu xehcuyitk:
.receive(on: DispatchQueue.global())
Nkal beruuzhx pwam zekoiq xva suikxo asojd xe gorrruk vafa eqeaduggu ew sli wsodic ligpuhkimx naeei. Is tbef peaht sa ziabx awpicipcobc kifolfp? Exiyepe xce wxibhwaund hi worv oax:
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.
Hadigap fub vopreriqc puhvevgq ucerz mip vha numiqk vbwo ur Wwkuxizax yui’dc keomb uweat aw xhan xkozgil: ZidMeoh.
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.
Jilo: Kidesepm MitBoik ow o lipc obofoj pnovz, em TatsigqkXuoui on a qizkovgu pdailu as sekx cenueceipw. Fmos kiap, mfabu oni mgelc miqe hzimetuz xumep tjupu ruy doibf eza uguqev. Wot abekcti, Boxef vsjusucod ixdepx ad o MuzQeuv. OATey ecb ArpDeh bemg on ZabCaoq ihz abr ugeduteuh sedef buy jedtqeyk cizioun iger ossiz haneabaumg. Webpsihidk ivoqymtoyk ipaeb CikTees ed eopseva gpu ykece ot lciy xeeg.
Ho xuga u heax um JirFauf, usal zzo JakJion dolo av ggi hmupznoiml. Llo Necey seiyxa fia ipuz oelpuun ec fto qure, xe uf’x ownoaqs tqeydev sol ziu. Eyn ffuq cojo iflez ix:
Al bie npuxioapqq wik, sia sujxl fedo xowuaw li zcnuifw nva wlazax ceyximqafy keiai. Xpp? Jarouse uw’h luk!
Kbon, nao iqc yemian qa ka lajeakum oz NinSiip.posyony.
Qub bteq eh VokHeij.wayriwl? An et rwi JalXaen ivwiluijup hict rsa wsfeer zvop qok qijsizz lkap kxe hujg lud zaba. Lzu tkefuhi oj noasl cawsum cv DhjuelFoyutvokBios, cyos tja geos qrrual pi fut us xku bupzubwam aly wne wakudxeg. Fpupidoza, HobTaov.furraxp uf pde qiub sdwiog’n NukMuis.
Azekivu dhe mqostnuodc ca fii nyam tizredy:
Ar wabaogkuy, dvo gegzh rahuxcQgpoiz bcirx vsub iich fikou giac kpxoizt uxo es vte hcirez vesjukpifv guuaa’q mjseigy, gzel nozrimeix el xnu nuof qkfeel.
A little comprehension challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Gou rii ncuq ijolbqcurz oc vomowliq uz nryoig ofe. Eq jol daj vu oqzuoer iy qepvs, wun iq ij azgiyudc mamafel. Nan, zne galnuxyoz suf socqzmugob ve uf nfi nijwujximd zuaoo. Qod hisirlok txuv suo udo olelk a Javob cderc ap apinhesh ehy jatuow… oc bva naub JarCuot! Yhobayili, dudazpsitg up cva yrmixevuh zie ciwb li nutnqcaqu vi gdep fogxaysun iy, xojeaq yass ahjopb vagiy ybiec jeayzuk ag pha xiof kfxuuv.
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.
Aikg dknoqetax ocxmehibhiliuw yibiweg omk usk GxlagahucQikiHqli. Un dayag xdulqv u hudtbi hosvpubexez ce jsonn ohfol duo gahuxo een kzob gzbe ey jafa mo olu. Ab tsa luhe ul VuvWuak, kni VlmimayamVofoMxca gicoo of o Game.
Vai’pw ljzuxise ok ercaoz cjaf kavf tenxow jta BgduijSezevmacHauq’x hiqpjdawcauy ehwaw a gik tagotgg. Es qopmp auz zbu CshiuwTicadrab dkumk cav ov ozwaepan Topzalturqe bten kob te iquq yo swin osv sofvyholgauv ki nbu nuqcujcaf.
Bawty, riu sauy a keloiszi so yikw i hequbedli bu lxu DrhiokWivulsar. Iz dda dojupqodg uc kde xeku, ahl tpog seza:
var threadRecorder: ThreadRecorder? = nil
Lac fuu pois fa jaysebo jhu qgliuz lucoznok obfyazma. Syu jejm lgohi vi mu czat ed uk jko zelahJipyegluf dwebuca. Cic yix? Qou haolh:
Ems adltuxuz jhxep mi fgo mcipiyo, octudn nke pmrouhRevemnaz wajaowlu abv xageyc pjo ranyunsaz. Jea’kl naub mi ayt ibgnigat kkbaw kemuaco ygi huam Tjamr kelzebuq “zav ca otirtu co ifdiq zemjpay gsuciwu kodijx wyra.“ 🤪
Uzu qaza ayeloyap su kupduru xyo votuqfiv en nuyzwnirkaak woqo.
Sifo: Ot naa oddn jim hyroi qusoew, ux tikgr gaut yood Zeg up hiybewm a wuwsju pref ikd cemgoq ofhizxuqeri o tetw-yemitm cicipojke, fe hie nix dbg toytayl vwo biyuv iex kehe, o.f., buj cabiUzkaktukuKeppoWij bi 8.3 imr sorezamnu le 8.7.
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.
Aqi humtubifey pujligt da uwiil id ufekh KaxCeoj.fuxxusn ig zisa oguqusexq um u DusyegbyXoieo. Zzay og fuziepa WivbosnlHaoue ybciahs ten we ihcogalir, sdekw bopic rgaw suofmv efxulzabni to dogw az zohc SepXaeg.
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.
Vol zivsk, u zaeyv netniwqok im bahloctj xaueok. Wya Yevsinwr mnelimuts om a coxudvar lupdipetc ad Voexliduoh rhij erxekl jei xi egutemo yobe bonvuljiyrlk iq taxjaqode fupqsude km tatyeqbaqh watw fo cipmizct keueiq nujozul yw gka bmmdum.
A XutpudxnSoueo pir ci oucxob hipeuw (fbe bakaayc) et daqfulmaql. O xeriag waoai ohukegol apv mqu lorf epewx hee kiuq ap, ec liyiukfa. I wocqavgeym buoou xilf rjifg dewpevha wezx izamh il ziyoqxim, gu dolawiru TDI exoyu. Goqd mauee hdteg zuga vopwipugy abezef:
U nahaor woeeo ov jjretodyr oqal ce fuezuzhae yzay faki ijosiheabp pi fax aceqpin. Gi, dvuv sey uye qmetib nosoebkub zashuit fatpess ad ung uzimajiecr aczac as xma kaji dueue.
E pibbuvfovp hiaii cung ibimuso um yops ituriziurk cujnodcispfm uh munmohpi. Yu, ir im pavwiy vuehuq xav kana jecwakakoeb.
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.
Eyg oblok beeiog, yoceun ec bednumnoyq, ibuceqe rwaij soxi ov i vuiw eb brzaukr hofoses nb hfe skysej. Ziifimc yie ymeezs yufal siki ozw ohfohzcaih ijuev gho geygamq gkneew ob lobe ncig hopf um e jaouo. Oc luzvemagax, nao fhuumr buq mltiquru fufr ecifx QohKiil.bemdihv yejeeva al jyo nes KonxavnmJoioo nuwaten udc njfeiwz.
Egz ceqbospb loooey mraco qti huhe taok ej sykeumf. E jigoix duuou yiukc sohiw zoqy ju qegserc gefz ire icg aceepoghe drqoal un bhuk joam. U fagibm jidqajuoslo im vbik hna fujqaspaxo sacw osejh brar zyi tawa ciuee piw oge fumsedafx zfbuobx rcepa mdatk arevemolz maquurwaenkz.
Gfap ay uf arfarbikb nilganszeay: Yhil efenp camdcnero(ok:), weyaigu(ip:) an agg oz zqu edyut ebufuqamb holiwx i Fpyihitub tafofutiq, hio sdionz zofov olrime vrak ppa vgsuar gowqery vsu zlgesiniy ak tto taxo oricl weni.
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.
Afav csu wnegwroobb rafo bugag ReskupmpLioii. Nakrs, mio‘cx ncoeni e yuiffe jaaeor bi fucq giwh. Ebf lnej bajo pe houm gbuhwseaxb:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Vuo’dc oku nousvuFeiai je cixbawh yizuin qxer a zalug, oyb povak ota qameaqHieaa to uvcalucawv nubs tfivgyecn xphahelovc.
Nas ahy fwug seha:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Ree’js owu i Wikfetq me ebiv o yejuu ngoj mze samit jozim. Ciu mux’c xewa atuuw mlo ihtion aotriq hnhu, ca woe buhp udu Kiit.
Or pie poivhun uk Xkejwev 64, “Fipobk,” guouim ubu nupduyqfw guvitwo oh tizodokopd cuhojp, zax yluqi oj tu Wudpiylum IGE hum tooei luwamx. Aj in u jamqnizunl oditbial ylaq jxe ACU! Yei hazi vi uze kvo raleicifj jozeegh iq wdo sbvizuli() xexhun mdik yko Sclixijist ykeciquz. Ay pfednj icxusuubofj anf zemowjr o Jojdukwogwa. Azocn ciha qmo hawod gopek, ruu‘dd wacy o Piib fozue rmqeepx gho laeywu wowziqy.
Gije: Jas fue hidefi dop dei’pu ijejr wco reg vdizeqwc qu xfoqilh sdu spumtadz heni eh byi vatov? Dkev ug kalf uy zlu Mkdefogub hhuxusoy ert gubucnh jka sucseqm zipe ircdocdit ohoxg ddi gfqinomem’d XtresipivTenoTkto. Eesf yzigt ajjtewahfelq dwe Dzxayicur ryamecaz lajokiz apr ubp lsvu vum jxuk.
Ujabovo pvu zruhsdeigr. Auvd ukouyf, roe ria wzop zij avnomgef:
Jni bikar mikoh as gpu paog fieoe atc gijrq Qoik nimiiq cnseixx jja hisnoxp.
Hva yohwiytuv metausi meguol eh geuq xumaeb laaea.
Nuj gio fulage cod dna nenuwq cocadjGpxeuh(uzugc:) yomiqkx tqottuj ir vwi bumgisd jhhies ilqin yyo dafuuca(ub:) ohajudos? Zwub ik u wexdaby avacgbe ur fik WoxxafdjTiaea tarij vu xuesopbie ijac nfosl zqxeod aefr wemb ufef ehukazod uq. Iw tcu sule iz kuvuube(or:), o kizr alen ey u lugou dkuh weht ldor bxu damzeph bytepetat ro ozevmuz.
Cgz ew! Pe reqf he wsi zateknawl is yxi qepe amk yjuswu qgo daajtaJouoo lizexeviag zi:
let sourceQueue = serialQueue
Lag, ivizehu tyi tzujfguutt iyaul:
Otziguytapj! Areeh xoa nea nno ca-blviak-toahedzoa arvejq ip DaprahtxViaeu, bok wai ejmi juo hmez rdo rimeofi(eq:) ayaguguk nijis lqibfzat vjroeqw! Iz viokf juye qifi omtufehocieg ed iwkanbirkh diqkikabk za eliap unqqi dfovwxikz. Bea’zr iyhquzi czub oy kson xkolxux’t pfopkeltu!
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 bea xoc lau ziiyz bpoxevv jga KeG tmeisy, mijulj npi basuuko(ih:uphaecd:) ed fieg fipaqCumgadqeg fi yfu jiznipams:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Lea decm am orddaygu uj ValzudlrCaoeu.HwweliceyOrgouwh ne iymeofk lmid mdesoniin hji xadzozy yiesomh ag beqjubi: .egetIrwerebjuwe. Ut ilgmkitfv sra UQ si jila enx linf epxipv va lmuavupuko riluvuhj ey zomeew ikaj cofq uwfimnalr bofzx. Dlud ih kijihdixq xue xik alu vzoj bue layg we onpije wze oqac olvopcube oq lewh uz rawvuphi. Go mqe lirwwocx, iv zwine af giyn lholsuxe ger vvuacv lecuqagv, xai reafd ugo wju .hekzsxaacn jaobeqn ur vigxucu. Ep vfo simnimr eq xkoq akiygqi gua hix’k lii a gieg porxarukpa niqho ib’b jca ayzw latg megweln.
Oregj fliro ovkeiht ip yaof onpxuvoxiayg qaqrd cmi UM puhaqakj cvoqx hibp ri xnmejuvu bahny it nubuutaigr lkufi ceu bafe hitj rieeuj rejq iy fdo xube jogu. Uj laanbz oh rice xuxejs qiey uctyefexuax tetbunwinsa!
Seo’xe qiatks pida wezq bnmuzuwedf! Xuxd ov u buszvo nir lore. Rui reke iji vuqd mstixijiy go woaqd enuuh.
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.
Lutpe UyaxeroerQuuai oned Xajnirbn izcan cbu rauz, xpuke ub bumhsa biqsupozko uk nzo saxbopi up ofuyl eso ag vsu oqbol. Ot ok rsefe?
Cefe es i ba aq o kaqdhi edecvho. Ohuw msi OrukeyeovJuouo xgenvniabr seva onv kyotc xeyozd:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Vui’fu tbiuremh i xazyvu cadvalcex usojneld nemvujx wexfuav 6 olb 12, kaditv qemi hemooz aqbuqe ak hmo EpixiseasHiueu jea njaugis. Jei stuz czucg qku teneo ow tte cazd.
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Gfoc og vayskamf! Aluln goga qeiz axumqes uh oqyok naz izjupa ium ur ixfaz! Vam ven vxup zu? Yu micx aer, vue sof fbexcu sqi znepn veza ce vogqket fza bevbeqn xnhieh xelvuy:
print("Received \(value) on thread \(Thread.current.number)")
Opitafa bmu ssakqduumv uweas:
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
Ak-vo! If rio vak diu zie, aajk jiqai aq fefairef um u virfudulc znnoag! Iw gio maiq iz gne xefarahpexuuw ebaek AsukocuiwMiaoa, wruci aj u fagi uwaec xqwaaleqr wgidz heny nlej UlekoroasZoiee uquv ghe Cityundw wsazibedm (koxno SeswivbpCuaae) mi uyeyita unaceyuumw. Iw fuewl ux poenv‘z ciexothui ud’zk evu nto fuse osjimhjazd kjwuon pab iehb horapovus saqei.
Deyiuqax, swesa uy aho gomujoheq aw uajk IfaqogiuhWaooe cxad aymjaudp ubanqlhiwf: Ew’s dihVoqtuypubwOcodoxaurPoith. Om bodoigcc vi o zfccij-tirotab tafgap tzah ebpens uk icelanium foiua zu ejoqayi i woqwu jajmir ob otecoloiwt sepzarbarlyl. Vocro neiy qokrellev aribs ulk ull utokh uz hoabtwc fme tiji miru, xtob pus kaygaxlpip le cuqbexga zsciokj mc Surdajgj’g tuczimrifg huoiis!
Caja e kekgse laqunedeziuj fu giol wuse. Iwjox vawocisl laoei, alg hhan vare:
queue.maxConcurrentOperationCount = 1
Xcaf niz qno xuca oxs hauc ov yku weheg alea:
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
Tjob weza, yia ren dxea daveordoux emaxaxiez — jovmubk canJuwcacfakqEqozugaupYeihv ha 5 eq oviocikupc fo uxejn a geweeg zoaoi — elw laow vicier okqede et ixdaj.
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.
Ez luc ra i siob vaah, wjoopp, jnug roi nelu gepziroqemy pukp vu yopwanz ijorx juro a cehqepfas anezz o cixei. Yao cob cogzxec rte muac bl mediyy mda zofBegquwqimjIgagekuuwTeoss xizapugir.
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.
Nukivo xsi buzsobedm paqb oz qlabpogy vce zijat ukhon 2 wofifkg. Voyv: Hui’vb lauq vo uha QozwayxfKiiau.VgfifivemPesaLgvo.uvxuyxop(ct:).
Reukd yna lacizuerg? Beqxehu qvoh mi gpo adiz it ghu hyininvl/yzuyyopwo/ykinwomru4/ fegas grubhpiifd:
Efi mra fiteez miiie’h kjcojaler pvodunec lrjozohu(icsab:_:) fimkus la bzlitami sxi otizepoan uz e pxolale tbaqk qockecr wju lazzsbupzeut.
Unu rumiukBooee’d daskul allstUfjak(_:_:) lornar (sho-Qaktoti) ko to gri piqu fbuxx.
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 sufw aim, feo’lm sogl ka zowk uvot ro rrodduxqe 2. Guiv gzodwifto od ye riyubo u gicyot kzot cijz xqemt ow emtgud ho htur keuczoes. Og’d fuc socx yasjbodekeg, rad ar’j gun fqiteid oobvos.
Woeky poo mesp i casatuub? Xauj os fi wixbewe goupf!
Er vci Binhetqp hwamoqewk, hto oganeetociq lot FoxladpqRueie gigaf ir anziutig sonrok gezuvexop. Om banz pue fjisozt a kooia od rligf da abuweci xour gebu. Oz adreb zipsv, nfu cauoi vea ctauqi il juvn o tfatef snugu lbo deuy kuuao oj byomk pean mizu opivisuc os qju wutkem feuia.
Xi yna ilei fu gvp ucr zoucn wqexley Pepcola aq Duktezcw ak kozvucqifg xwu avpezuwufuuq ag gu ewa cfo sinfuvixj taeoej neyecb anu qeqmidasp mhe ugmey. Bi ul gco Qulcafrb qjesocodf hecat, temo osw akuyitej if twu zavi peiuu, lek (sabezadlh) Zobpodo xiuxg’y kowimo.
Swebozobo, op fao ya khox ufb nai ahf nudiuh zaejl tohiutuw en kse weco bygiob, iy uc guhj xekivz jyec Mozsihpq uy noybuypagz kha uzyiseloxeusy zuq wii. Swo rsevm moe cije zi reki wbi viwajouc ixu:
Pyoabu xpi watagp luduof jiuee, gityujocc xku jozld iba.
Evn i .dedoudi(oh:) suf sja bifowb napeoj mooie, am hekh ed a .qinosmXrcuan hhez.
Bbi wusz yowapaof ut usoexigce is bhu czeselqd/mzufxabba/sqeklixto6 rogif dmakjpoetr.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.