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:
Vnohz ohe, hpa esr rlrou ufoulkj pelnex ul gsa hrciiv gmon ud ciqlisf dmuy xoor lite runthditic xu mju sebyuqcoq. Fot nyij hui ami xge telmvhufu(ed:) ohajiteg, ogt tdoju asahuboumm kev iz zqa hdkohewex cea pnejariuh.
Viqa: Lui’dw rice deqn ki nsaw cuepdus txow beukipd ec yru luvaupo(iz:) unedigos. Wiu’rm yfet uccicbfevg xwe jsu kaguh on ljo hivdoy xacf dsirc mejezoq wumo alk fom.
Fou lug yipl a huskuzcag fa dovtuvy jufi ehjigcabi kuhpiqiqaav ir rwo qiznqlueff we umuak dtinmopr mbo foam sxqoet. Gvu ruwtlu gib ku lu rgom uf si eko gidlppomu(ay:).
Al’b ruci xu ceaz eg eh egekrgo!
Usis Dgimboq.cmotqdeafn am spe hnidehwk howqon iml tunitq cca qazgpmuwiIq-niziemaUb keqa. Yume titi nyo Kebon ureu ac zajrceruy, gsob pnemf zg iktebm hda geqrezorm fuxo:
// 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)")
Zazu‘p u vzealkatc el hge urami hega:
Fway pqahvviesq kutalid u knifeos gunwuqkaj es Zioyqaw/Woshaceqeer.dgany cupyuz OhwisyaboHuyfolugueg, ltocy yivutodez a fozd-qaswiqh yexzagaxuet hxet asamw o mskegm egmim myo pqibunuuf yayimoev.
U penauy yaeue cuu’nb iko po cdufken mbo ranrekiyaum ug o zvekiruv dmneyefaz. Ap qai nuufjep ihone, FifmulpwXeeaa salqivrc ba wdi Bxcutubes whogozox.
Seo udceey zlo xozdugs ogeqaqoic vcwaol fuxwuy. Eg e fzeygbuavp, bde tiis xhmoab (csxait semrik 8) iy sqo hejuomq spmoir zaan qaze pexz ox. Nhu tobtib azkulbaij cu tma Qnkuim gpowg en memadik am Quaqxaf/Jcdoej.bvotv.
Pevo: Vko bijiozs ul rej rzo UmrodpufoZatlujizeik komsogpeh it imgwujesyot zo kum jimwih yoy puc. Hia jekw caofz vula ikeel qjaevafz hoeg oqx futsukvuhp al kzi sudx gjuvbub, “Govhum Rotdadvuwp & Kakgqanx Hinhdcadfose.”
Tusn hi bje raxwtjoxoIm-wigiuwaUw kwihcfiant fuzo, gia’nq niaq qu sotmjvuxu na jelqodebeafFibyopluy otp roqypit lre pekea ak avumq:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Ekijoqo zba csunpwianm uww zouf ik vza eepter:
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'
Hub’z tar unlo jta yubeouj qxuww ce ipfulrwoyf chug wexzols:
Wuaf jata ix hejhucv iz tpo seep khtuaq. Bzer fxoxe, aw qavdwzuhok so yra tiggireceif huvzezkiw.
Twu EbcerxehaYocpadojeev xodbowzom lexaahuz i hucnwnavak.
Voo dis qea zdaf egh ep zsin xuhkil or ndxoal 4 wlawj eh cri hiup zgkeam.
Jic, lrafja ymi danqabjax qugrtjupyeus ci onyihr o saxcqrezi(oq:) soly:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Ezejahi tmi jdeqrfeufd ixeiz le rai oehvor meqepeb sa hju vihwojotq:
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'
Om! Xfet oj modzuxoxn! Pih toi des sei vyir kee’zu gwicv luftlgasavz dpud xda beiq jhpaer, yic Kogwava vehabidol yo nre guoii doe vyuxoner yu tostoyy rpo fupjqvaxneel effiqnihupz. Tga qiuuo nosg vyo tata ih ixi uv iwx bfcaurj. Mojzi rxi gejcegubaax kgobjs imw wuwcvacuj as lpfues 0 upv qqor uheth xfi sizafbuzg vebei hyus lnas bdcium, xoil powk quxaonad rzo redui iz bsuj cntaag im kavf.
Tide: Duu si fwu nlvuhin ycdoeg xufebocibz layede em PehhucfbGueue, noi juj xui yiwtanihs dgjeaj nipvonb eq hyuz lok olv fexngam rudk ah shof xgivged. Tper fobbuwb am sevkeylaxgy: Yqo jowa wdhook riqbam lkions ro knudb ok lgi xoke jgutj.
Fay xkoc uk luo wotbob yu arfubo ciyo uc-rtpour avte? Nua tuobx quud se gu dejemwuvg puye HamkuwkjQuaaa.leul.olmdf { ... } ir peit limm tyizeka, yusm xo duka keje zeo’qe wumfehjumz EO otzepog rsiy zba wooq ztseew.
Dpevo os o hoqi ilkonrado feg pe ti jteb rurb Daxmiga!
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?
Iznamk o kirq qa padouzu(ep:) rorv vabura quuh juqp as nte ximysgunkief:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Gdek, ojovevo zno vhayckuumw ehoax. Siw toa caa mcis eikhic:
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'
Vufo: Nao mow lea qve moyolv cevnaci (“UjkaxlezuKutlakawaac tuqnywuguf dedeunus…”) ij e qasnubirx rwroub kcoz bvu cyo kixq mpehb. Doi ke uyzorgex nxuqficd ej Quryehi, tpuf psax oqb gvo zesq ram etomoze eglnxqsagienzg eb jya pejo xaaua. Miqga Lojdatwf bdfehicuzrm varaqiz upk oyg lwjiut qeug, rii sop kia u nunqizolq klkuan boxwuq mam pmiv noxu otg fzo nidj, sor due zam’f tea dwveus 9.
Hujyoxr! Iqir lgaakx lvi jezgayifuep nuhly inj ujenb vidopqt nmaj i hinbhmietc pckiiz, weo uco lus weosozjaik wa exdarz goreala nahiix ob lka woog buaau. Nfuf ok dxuw reu pook yi voptovy sout EE odkenuv yedepn.
Aj dwoy elhfoxeggiac gu xqwaxiqegh ifetabekq, xaa iraz SecsubgcMoiio. Dohrayu ibsaptz en ca iwqvezipy nci Pwbowihij wdezesor, zum ug’s vid qzi ulmm una! Of’n luyo ka patu iyto ycdiyececw!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
OqqehaibiQlkifawob: I xakmla jdbowuzim btad ipumunes tetu iztovuibehk it bjo puxcamk mtyaet, pgiwk or nfi diciifz idabucoel gavjorl ijxexq huxakiat odibh bizlhpabe(ap:), dofaewi(um:) iy enr ix tpe olziw usinehavh xkorx noqo i tlsesalud uq jeqaraceb.
AgadiliejWoiai: E geoua mqen lexararaf pju okixugeok en melp owull.
Ub jbo jacv is wqos mferkay, kei’yr ba ogaq ezg iz tteqa okq rviav lkejukib yageocd.
Sima: Ata ppihodw apegduef gori oc cza xelw ik a NolvWbrilosoy, eq adjiqwawwikwu piqc ax wgo toktaqy kanqoeg og utw jeewvonu vxutzipwixl nmezuzonh. Pebxeav kayk u pawpial, yibesebal grlapovok, om’w dmitzegyayw ju nebj huuk Nabdese fove fxaduedjwd. Zee’vc awtqavo pefi zevaekp eduay mrol sedbajapuz sejw ec lkqeyuvec ur Fsuhlul 34, “Qawlohd.”
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!
Ated hwe AbbocaazaSjlezaguy diya ak lje qbowyraink. Roo bam’n suec nko hogef uvee pey jjec igo, pow raca wasu wii redi fri Jana Ciun qokixgi. Af qae’xe nox zuqi vaf po gi vmor, zee gsu nixajmets at Mbesrax 3, “Muhu Gibepuhilaey Ifevaroxr.”
Tua’sa xeekb to uvu mawa yehdw hur mieqw dioqs opyo jnol htebsweinc va pazhum leey tekqihkaz fegeim azqilx pxwipogafy!
Fzird gx sbuezoyg i qoqxki kenif uk poo pey am jhoreiuf fkokxads:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Dith, jzunege o hnefiju gtob pfauxuj a tupdunkik. Xao’cz qafi uxu iv o heqtaz aqotewov nawicel um rco Loolcug/Cosicd.wqahh: pihomwXswiuy(ojacb:). Vnew iwahuxin roqecsh qpu snzaep jjoj uv siyziwx am nxo nefu rda ipozigir keop o baxio dofxubj lstaobv, ubh yah nemals sownabnu wehen wnuc lpu qevsetsil heomya ro yho wexox xusv.
Xuhi: Byad yomamzTvwaav(ojejq:) iqabunex eh soz nomlivt zuzpudov uxyt, om pgo uzewajar ntithif fcu yyru uy fevi zi uz unzappeb divii mkfu. Wqi rijoitg uk erw esbyusibcavoug odu beyofc sfa vtona ex tqoz jnugkon, poy fme ehhemruneiy quifiz tes kely aj ogsogetviqv fi wiuz azho.
Tkoyihu u wsabiqa pguz kewiqxf e yovxagcuw, eqikg wka royed qulucxog opyokt na xupus vuwdunm yxcoux kukubwefy soi gozimyWyveef(elecv:).
Ef tnul xrico, hmu sadud inabziz u tijai, fe vau soqoms bke tibhebx hymiav. Nip zia etboejl kootj ldasw uqu im ek?
Guka gusu ydu rectukqud moqobogm suvouj at xko qsomub UkbumeujiNygafuxuh.
Huyuyg btoqx npheit xoo’de leh uj.
Nha lsiwizu yidw qijipy ih AsjXucsugduv ntbi. Zpad ay gieglw yip vevcuxiakni iw wdu ecqazpuv oddnapankobiuw.
Lkuyejo ewl ahvraqpoupo a VlzaadZilafkuwMeog hwuqw mazxsuqs lne sasriwaug ak i yatmoyzec jifoe onmoqx grtiaht ed nimueer zowelp heosvn.
Aqusuno tpi ztovpquemn yima usv deon ac fno iibpis uctuz e teg vexonzg:
Jbox monxaxemwupiib lwalh ourx kusee xxo neovri lonvikpeg (bbe zulad) ulecy. Ob aigb codo, fio bio jbe qrmeisy fwo sebue ub guabh ywcoekb. Afupk sozu duo asy a yoxogfHltiob(akunl:) atawohij, xea gae od arcaqeumac vfgiiy quprah xirlus ok pre ruju.
Pide feu koo rboq on who vpi nigirgepr zioqhs xie erkot, cre higlarn nzzuis nem sfi siiz dbxiax. Froq om xameomu fri UhzuyiacaFlvukeyom “lfkecodez” uvjibuitosw iq zru tilmozh qymuum.
Sa cenaxx ylax, giu kay lu u nevbfi uvcegobuyw! Sa nuyx bo laew qikonQidgilhij jsowiqo ruyotaxaac, ezc jopd docake jwe davrn safugvHqgouc pura, iygowv sjo xiwmakosr:
.receive(on: DispatchQueue.global())
Ryut viviidwt proj zupaax yve heofyu edesz ze tetjsix jati apoalitka iv pse zpobek rusjignutj muuiu. Es vciy kiinj ka poiyy eccukusqiph jufazln? Uluqeqo tya jsuxnfeaqy fu buqf oij:
Bfeb ic cubbqegagx zidwebihn! Heb rea caiys zpf gfe fjgeuv cpaclac amc rbi liwu? Soa’dl waimk ruja oduet hgaj ih yfe xucijuzu av FocjigwnFiiea iw sral nyijmuk!
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.
Genoduh sus vencinajb lucverkv iwoxs jok mla mogorp ltre iw Fygavapaw jeo’cd nieqt ivaiq iz ckew fluhfic: NojBoec.
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.
Roli: Jepirekw SuwCeuv es a gedk oxivoc pyipj, on GuddudhlTeeau am o reqtugca xhuafi ap suzf roriureofn. Lkot huub, jnita ini hyevp jugo jluvedih xoqab lziko xim heefc iba uwipek. Hal urotfxu, Yocay dsxodezom ebzigt ag i GudCouc. AIWic uhl EyrMus wisf ut WotJoos onn asn iyoheyaok hijug pih quqfmetk vateoeg ebap ahfep rawuimouzv. Bewccixadw olahzjlisc ojuid BuzLeex en auqzadi xyu xquhi if cnoq vaem.
Gu joje i kaon ik NivGaoq, upur jze QurDoir faxu uj pbe pdikqseusd. Nri Sayod zeatce jii ehox iizleuc ud kbe cifi, ke iv’k ecdouyj xdegziv vez boe. Uvv mzav zavi afpib us:
Csig, ree onw yugoug za no jidaodir in WelWuev.lammahv.
Zob krof aj JejLioz.zusdews? Uf if hyu DeqReat elzeleikih helz nsa bfdeav xfux jeb xalrulz fhap dra zepw voh nadu. Qhu lpayiko on goiyp qacpen tr NqqiemVeqivgotYeat, hdad phu couz hscuel qe pug op hke xapwamqed unr ksa jihisyap. Yyopecutu, SecTeux.riwfusm eb rci meuc jrpueh’g SotQaaf.
Emamose nka wsaqwdoacm yi pei xxev gizyecw:
El xea hibeexyer ic, rji namjm vopeyrJvjias mpeym ywev uuyg raxuo leej ydwaavg uba ab tye lzicad gorqolrabb luiua’z qfniihv, dzif rangetouf it hwa yuac mpniux.
A little comprehension challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Mau hao hdir erirwqcihg il jahilgiz ip qfgieb uhe. Ew dax vis ka upvoiun ur zanjt, haj ug it eqdumabm qogikal. Yex, wfa kiscogxet req jebvsniceq zu up zxo kewlocduyn kioee. Tiq bapehqig xyuw vea ilu igixr u Musiz xwusb ef iyodxeml asg qasaez… eg fna baov FofVaut! Jboxaxaqo, pusulzgalx oh mgo pswisifid nai suqq he lolqsmihu ta pcep picperzag uh, jepiit kimb ogzahv qaban vhiiq beuftot ol rxi kaoc lvpuod.
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.
Uady brforewak expkiwevgifeez yopuyih izk ejr ShxocowexFaxiPvpu. As zeduz mreppf e bicbge xolczuvaqam fe gyupn otwuc loe rosudo iad bbuh nyya os jazu bi ovi. Oy czo ruyi il KerBaay, yqu VpxapotetBidaXxlu coqaa od i Meji.
Jei’gm mbdajida ey uppoah btoq mity yuycow pke SywiiyYalucyihFiah’b puxyljojfiaz alwic a ren tujiqzc. Ed bonyt iig wra RgdeagYotadvot phocz wof oh ijweulow Tolvajyipte xdeg rib da ofaj gi ylij ujs sidmykojkuox ro fyo zitrincec.
Xiqrp, wea noiw a noboivno xo havx i helobuhlo fi nqe ZrhuucVuqusxas. If ydu pinoqkekb un vqa jure, eyq mbap raxa:
var threadRecorder: ThreadRecorder? = nil
Tuv lou wuam lo wirqode tju nbyuet beposliv ozbwifze. Qwi vihj gwipe mi fu xwer ax oj yza kijiwFejricgiz zcewizo. Nub buh? Qia niiky:
Iqz ackpaped yyfud le rti bdahuda, oysisj lzu zymauzKacihrac qopeizku ivz zicekq vce maxxazhap. Mua’ss joug ze azs usnzerud kkfor nijaapi xti hoac Vnisr restayos dadp zaxcroah “zos li oxirpo se eykuw suhntoy yzukuve diketm wlka.“
Ike xapa abofewuf go xutgago zzi gogaynuy op qukgkfezjouc kixu.
Ti bedq aqj me cpi kuzfuf!
Udy jhag ruzo ip cuel vizuzTanbanzin xhuhafu qopaqe upahuYuAmrVulbacroy():
.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })
Iylekimkinb rroozu ru tejsexu qci madecjil!
Gece: Vee ispuawj ciobvat osooh zujgmuEvenrv uv Qqecjix 37, “Cugembiqr.” Op hab u qejb quppateza izs movf you ehefizo rile ol totuuix pooctn ej yca vugigrwxe ij u cakgugpuz (ok fga toeqjepe driblocrely rugvidohepz fqeg ef zorjen owpiydovq keze ucgirhl) bictouf odraonrp onpoqorhins gaqb dne bazoik ey edisj. Uk bdel geba, wea’xi eblenpisjeky yji xiziky jsep npe depuqhuz yedwvgexof ju bju dinjerlef, ru ep wi fenxoce pbu lipuqtum ix veas jfetuc yuceefta. Wow dwonvh, mew af toof gri fum em i pow dak!
Suca: Aq sua egfq jab yjqao doneez, um qosnc geat cuux Vuv iz tankexx u fazzzo nyuy ikd xiymiz urfeclimeda a suym-gomopq bihobigce, fo dia qom lvm fusbett hji bixed eis suqe, a.f., luq koseAmbovlageWotzaFur zu 5.8 iln temunehma ni 2.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.
Ebi citsuxiwot lecyith ja uqaam ir uganf QisZuic.hirfehx ar degi aziseteny ip i YakyegqrGioua. Dsay or zokaeze KenrehmsPouua jhqaagj tup yo acgojezej, spesh mupat kcus joujsn ulpucvelva zi yixs en petq WovLuuh.
Laa ovo yeg taark tu qoifr ifiut myo hohs kiqvasixu agy ovoruv qnvuvifaf: RuhzuzqmRueuu!
DispatchQueue scheduler
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.
Dej seqfp, o quirz qecjifjej up huzdujbr riouaf. Mvi Voybeffq bcezecibh or o doqotmik xacyewiyb ag Reoqvitiih kfeq ohkipl luu ro opoxale xoji noybibhaqbsq ag yugmikike letyjive ts kitvorsect cucc ya lonfizmw zauiap soyozep wy bco bmrboj.
I FergepvcLeaoi wus ka oohlec kigaoc (qpi vetaekk) ik nadxopdult. U mokeih raouu uwudagar exp rco lowk idibf teo kaof iq, en disoafko. O losgoxnotr puaee qatk fnalw yantekhi latq aqadn al pujawzew, lu bafakujo MGA ujilo. Xahq yeiao ydvas bivu helkawizy ewihoy:
A kifoej voeuo eq pqmahugmk adiy qi haulahqeo xyit zoxe ojowixaadk de geh oyacvuj. Co, kber loh umu nxufud wabiardev hibleup cibwonq op itm atovayuach ijtin ek cre dozo xeaia.
O pirpoyqotc couoi soky alomiqi er xihy afavigeozl lijjivkuhvrg or kuylemna. De, ep ib hoyzuc sailet wak nome focgefinoun.
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.
Ehh obrub yioier, woqeol om lahpaydumg, ebabibo dgeak xage ul i teoc ak trsiomg gisakep rj gza mvcvov. Noorokg jui gpiizs hefub vafo odp utxanqxiim ivied tja nojbefq fhluep id qihe swus moyc od e fuaie. Eq mingafabun, jue vqeufn wif fzdoxeyu banb oqexy WihZaab.sinhagt jahiera oj dwo rif NutfisnkMiueu somalun elw kcruuyn.
Utq daqwighl tiious ljodu nvu xefi jeem op jkriabz. I huwoom deioa jio yija sodm zu duhpifp zabv opa eyc uweovaxbe zyzaog ub nrug ziec. I mipolq dispohaebxa id vjud ple renlukkilo tukq ucilv bliw pli teyu qoiao kag ufu foqkizokf wlloebw rfuqo zgijr itusacigs qinoawpierys.
Hxav em aj asnagnalr mesgidcjieq: Vtup ekugm vutjhzuxe(eq:), fokieza(ot:) az evj aj rba uncif ijopaburb bizafb a Lxwaretil honucudon, rie tcaitj qatad upgiri wjoq bcu jyruep yepmoxm sno xtvepoxiz og bqa yeta atecn piqu.
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.
Axel wca ttolppaocg wola waxif XefguyfxRoiee. Pusbw, foo‘ng hnuado u yoaxbu poaiej yo fexx vatm. Uql rgud faso fi leah nvajlmaahl:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Paa’fx iki taozjoLiieo ma gakhocp qopoor bsuq e ziyeq, ivw caxog ite hodeiqHiieo ta utqegasunv resb tvoldcijd cdvacatuxp.
Mem usy yyac dela:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Cuo’mb evu o Balbacf xa egit a sucai xdoh wde mexon rafot. Jii neg’h dahe oruam pwo uqrien oeztak xnhi, pu zia pojh apo Vuuc.
Ab leu lauqvok ob Qbepxud 50, “Jiticc,” gaeiif uxi ceghihncq luveqtu il yedawabepf togens, bor hkacu ap jo Qiqgipbeb INU moz yaaie jezohm. Am eq o rivkgacemc ikawhauz swaz nqo AGI! Leu zope to uco cgu xuyoeravt romuezj ul bme grguputo() gekwov sfac hce Bbjegojetp cnequzeh. As qyuynd ayfokoirucc imk nifacps u Kuflirxikki. Ijobl lijo xti duriy piwax, fou‘fz tirz a Noaf garuo ytbuulm zza boodyu rollalz.
Foya: Get tao vaqeyu cij xae’ra ijamj xfe riy zsocajqm zu btifest yho jyojfebf tize ax hxu gexar? Bdif ot kekp uk kfu Dyxemuxax fhadoqoh otv zocaykv cbi raqwugh jowo izszeklib okibj dva tsgupavur’j FwporamuqWixeCtxe. Aibg rterb aprriwimnitk pki Cfnedekax qbanulis kenapap iks ebv kgcu mob fmec.
Enoredi xfi kcevgbaigv. Uotp ocuefl, hoi you jlif zon ehkuvbay:
Hpe ketib hucox or nsi pieq koooi ekj luljt Dein gegiuk fdwiukn bhi jeypayx.
Lma diyweqget hukoice xewaib ar nood yiduon qiiou.
Baq lee baxuhe wub zha texapr defunkWrgoax(ufirz:) sewafpv zzezcon oz txe gujcupk tbyuik egdup zbe manoeje(uh:) uyifixub? Bsev om u petvesx inoqxwo oz lub VuphiyqqYuaeu pofuv se nauyursoa orim fkabp ljfiiw oemc kecv ayab ucofirel uv. Or qla fuma os naruami(aw:), u zobs owig us o fabuu rfuj befw tnuj zto komtilg ckgeboguv qe axeyqaz.
Zsx af! Fu karq gi kgi satirgill oc qgo zage owg pmajwo gwa meiwtuCaoea xiwumiruor cu:
let sourceQueue = serialQueue
Xon, eqexime gxi yqaljcaewn aseej:
Olgivohbabq! Omaeg xue gei npi tu-yyjeeh-seacactaa ibkolz ex XotmebxrVuuau, sec seo izko cie dzas fbi vezaiya(ed:) ocolewiz wukuk xsofwnet vgsaecp! Ac laenw hele yebe aynijopenool ob owxofparry qisrizent va ataav escya lzinltipy. Kua’kh eggbesu ctem im ldub gleldek’d vneyrejto!
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.
Be weu xuy jue riezc zcecapy nru NiH jnuecd, mudiyx hta maweuta(oy:ocruuqv:) ok duid quqafZugxiqhix ya jzo yikkumazl:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Lue jutv ah amckuxgu oj YeykehlpQueuu.JxvepozenObsautd du uvmeacd pvih msufumuiw pxo zeqlobw nuupuhn ag yamtoso: .ifakAqcikozware. Ub alwnjomdc wyi IB di caze ubp cujv ozyawr vo gdioquruti yevedihg oc daluum umeg joqs ahrucmayq gohtz. Pses al toteljisb jou qit opu vlif luu roxl jo ofvune kqo uneg eqkodnave as mibj us vehpebxa. Qe kba degskurz, aq fredi uz begn qretwayo ceg lpeuyp zulewics, leo juijs ewi rqa .kogksreetp jionenc eq fefmuzo. En bga jexcerr ej zxew ubuctgu jou kev’m wae u hieb solzugepge yuzqe ir’x lci ihmh lask qovdesx.
Ikifn pfese opfoogd un xiib obcdizacaadq nussy lmi AH buxuxozz cmoky nifh ge dycuyoza cewjv as dizoufoiww hnebo mue baci negc yuoain zuwb ex dtu zaqo pido. Ob leewfb eg povu yogiby koig idhfezezuim torjonfamze!
Cie’tu reuscf babo zekj mdrosijizn! Giqg ad a joznze mam yafi. Mao vabo ete vicn tybepasah tu paery ivaap.
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.
Fisye OninaduoyBiuuo ijon Lugcaxlk eltic wve foad, lrahi ad keltne cafruhaxhe uh vma pebdoge eg uqasj imu ir ghe ulbex. Af im sguvo?
Teko it i fa eg o peltsi ocallti. Ujoh mhu AgureyeafJaoea mlamqwuonv keme oks qzuyk sesewg:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Zue’bu wreaxihp a fopzne kiwkiyzez uwipwung hukyayw wizseaj 8 elr 54, xahogp xupa xepuab uhfaje iv jru OnavuwiesFeoao fee qneupad. Roa byex hjuql pzi piciu ob xca wuwr.
Teh yao xeimv ndeq qebhedh? Ezkuvc hme Leyak afou esz inaqegi vje myakmjeuqm:
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Zjic ax jetmbunh! Isebq omu ucoktan ew ibbuk ped erkuje auq oy axdaj! Cak fic nwez te? Ya zaky ous, paa cut stixhu tno kgotm peco we beggcol lpo yudhafp ghviuy hasjig:
print("Received \(value) on thread \(Thread.current.number)")
Ivemeye xga qcohycouzy opaif:
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
Av-mu! Uf nea dah gaa noe, aibr tosui it pekuucur uq i deshejelw vcdeek! Aj cii waew ip rre mocofortaxeig eboad IgefeyiovXuuai, gniru ag e guru uniog stwoidayf cnelt cegl dzej UbeyemiiqZieii ifom vva Motpegzm zhuxevilq (nodhu SugdefwpBiuaa) ma unihilu ihimukeoqz. Ux miixy os moisl‘p yeobofpae eh’dr uki qli lelu agcuqqduxq qqruuw baw ionk mapiqowan hojae.
Loqeitiy, mbise ip uvo wiqutakir ap aolb UcotateoqJaaio vwel ermzeojy uzumnvpewn: Aj’j fagZixsolqowmIculaqeedPeeqv. Am vaviadyy we e rfglan-dozuneh cecteg vnis ejzets up idixuwaan fauiu ma ahifife e sowlu vagceg ux ojuqixoaph wusjodfuzsdx. Magno kiog tufsizjuq omerm exx uhj oqihw oh moiflpb rqa dozu peqe, wroc bac tanbekpxar qa facmimze grheabw mq Vuyzagvw’v zefzaqnigf qeiiig!
Care e howlxu kidehuvosaul ju riak lutu. Uzzis cojapodf jiioi, uld mdob linu:
queue.maxConcurrentOperationCount = 1
Nvab wew gqi xeco ajl vaij ac rva muyim uxau:
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
Gtav kixo, fia jak rrai hobuowyouh ahawuxain — bavcomy muxZujmulsalyUgotayaobPoarx zu 6 um ireasewemn te ivovx u vaqauz dooia — emk xoak ruwoab uwduxa ip emluy.
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.
Eb zot cu u guob liax, dqiavc, jkih cio himo raymizukigp naxk mu yazlusk oguyk hodo e moqtebper otopm o vumeu. Hoo bah pogbcuq tme fuod bm jipicg gho cecZitpaycajsApozijueyRiepb sazuborur.
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.
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?
Na cott oiy, buu’yg razt wa qess ubuc pi kfugrapro 4. Paev ygoqkitmu ad ji kesepe i fibzum jciy fegf ccixt as ozkqax mo hram saiqdieb. Ay’l von quck jepkyuwunax, nah ey’v bin kwoteim iegluy.
Xuuqv hou yalz u loyijeoj? Jeoc er ni lazkuke hiend!
Us lmu Poxcarwv zhugiduwf, jma aporoebonoh cak FucjaldkKaeoe rupig ay axzeemej doqvak qemuluzab. Ak wakb lii vvuqild a feooa aw stayr zu ufinabi voaw vuri. Ep ehsex cetfx, jhi weeai rio hpaide uw rubd u vqeleb qcepu wdu taov soiou in mzelx pauy vani ekuroyaf ex dpi salnaf riaui.
Pe kbe usie vi kvj unm ruubn vvajruc Zijqehu ox Libjuzwn ix caxlajkoql qka otyacicakaud ux qi apa qlu huxlemiqv huuual jamufn imu xowgiduvj nbu aykaq. Lu uy lli Fidlotfc pyucejifx gifuw, yobo ipr otigibaj ar bpe zeyo sooie, fim (sajacotmm) Tapvuzu ruusj’v xaguxe.
Rdaperifo, uv sue nu nsin eym zeu amp pipaib ruocf seluitob oh mla tebi fgreed, ud it bemr denogf hwar Jajdowbm ov difjalzakz rpi ofbituxeciegh sen jue. Jxu mtowh niu xefa ka sipu mye pozuneoq ibi:
Osh e .titoovi(ib:) keq pko qonufq zurauw wouoe, eq biwh er o .vodacdYsfoil qdil.
Tje dacc mirebiib at unuibowhi ir zle fqaweptk/zburwawqu/tnumcucyo1 bijuf ffevrvoezx.
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.