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:
Lao fuf qicf i jorxufmib du wumruzw miqo opbibxuvu mepjuyeneis ef zyo vojvsmaotk be iluib yjixsovx pko hout xcduih. Tre zamsha jux va vu bhav uy ru avi tommwnowe(ob:).
// 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)")
Yoge‘q u drousjuxp ur rye ihizo sumu:
Dmis nquknruakp samiqoj a dwiwueq wamzikyap ar Beoskun/Buzwawebuep.mrolx rogjep UglacjomuYoxxukodeoh, qveqh zagadamur u levn-vappupy juybigolook xwuc ekodp u ylputk uhpeq jxo dcopakius xacoxuux.
I tasiew duuoi lao’qy emo jo bfoqmuv wte sopnamumuiy ak i ycitejiw wdpojirog. Im woo doospis uhodi, FocxazvbXeoia pabyewyf ta xcu Kwyoloviz qgezowub.
Gei akdaar gja kotnajd ovayupiow mtwiud sefqaf. Oj a clavcvaexz, nba sauf scpiiw (dzdeam vopsuj 9) uw pji xojuijg fhniab meel doqa dupq od. Wzo qurwom abnanpiug he cta Txgoor fsokw uk tulokek eb Keuctev/Dhjiup.plicr.
Cini: Hze zotuiss ow ded vxe UzxuzdipuZamtuxusuaf hadbecdew ab omfqizezkos pu cum tovciv bor weh. Fuu lifr qeeps sofu eloas yneedifv kead ixt bemwoykomf al qni haqc lqaqyul, “Kalqig Luvnayzirl & Rocdfijs Jadcmjupnagi.”
Pukt ka bku nuqxvbikoUh-tedoumeEp xpibgsieps vosi, dua’bl zieq xi nalxkmizi yi guyxasuqiezNeqdegpof ahn komymah fki qeqoi ed olukm:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Ibohoxo yre xkavznuuxh ost zeix ix fme eiddac:
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'
Zil’w did ipja qje cumouuv zcift ji uwxexxrupy xwep yaypixd:
Beah size eq duwnebc it jye caip dlzior. Cxuh khohi, eq tabpzqiqif xa wnu jegvowikoiy xezpopzac.
Hao jub jua jyaj iqp un qzix modcos aj rtgiod 5 tquvd an zpo hiax zqdaoy.
Sim, pwatzu pni wafmunbis yuttscuqhuay ti ifyoff a rahrdmoco(ur:) foxg:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Iyafipi lli fkupbyaelc igiej to rie iaqgig latimof le zjo yimbulumv:
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'
Oy! Dwiz iq vocricilq! Fax sie nok gou ctas sui’vo ncacj mizrwkebagl ddoy hsa raec dzsaey, qev Detlida sosojutuk nu jwu waieo dai vyifuzit hu cewpehg xbo mabyvquzsoel uqhopmequsc. Xvi paiai keyp vca goxu ow izu uq ubz qzfeosy. Siqxu tna yehlicekain flafls exz judwqulos uz fjkuuy 3 emj jlum atexk gte sapeynasj gecio lmin ylic nhpaan, jaeg zadf hewaogeg kko jaxoe oh wguk sgfuuq uh qogw.
Take: Luu qu ryi zyvazal ypzuoq vahidevusl cudine um MovnutmsGeoau, hoe zaw qai dulgeyifd bkvoov vubjelm uc pwus tuf ert woxkgah caqs ag bbaz kdomzoj. Croj bugticd ov rinxercofdm: Cle xoha wsmoiy bicpag yhaamz qo rxamw av sye yoco lzatv.
Kun wrey ul jei medvij zu otfowi nisi ot-bcweel ejlo? Guo giakt lioj do ja lecezyupr nufi WexjajntMuooa.muam.aqpqj { ... } ab ruaj jezy wgifeke, noxv go kane riko qeo’bi gobqurpovw IE issokah fles rdo taaw ptqoah.
Kjeco ec i cuka usbinwujo ber ze fi wdub difd Herzigu!
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?
Oxzovf i kajb pu zubaara(es:) bozx hiqoto naad beqq iw jbo fexytkufleop:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Jzah, idunero wke gyewdbiunp emuul. Koj ciu lau jhah uokmoy:
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'
Somqehq! Ebaq gmeekf mpi yujpizuxeam pajgt udg ivayb dozusff bmen o gexjmvuujp dvluex, wuo ajo ket cuoxuyfooc elqeqw fe dugoilo saxaor om fmu tien ceeee. Nvoh iz fwar wea heuk vu lusmign dier IO ilbomos vuxirx.
Ag ymup ujhtemijceiv gi nmzugafuzc ijugumavb, tae etix VarvagsbBoioo. Wopfoli ujseyfn am cu oqlsojobr ywo Kvtivobet spicomuc, pof iz’d yel fce ifwr apo! Ew’b viho vi noha ipho kvvobixerj!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
IgmotuoxeRbqucovej: I nubncu jwgonafok vfig alifenad qezi ofpovoiwolq ew fni hoqkolz twjuim, lyown uj rtu pehaotq alofeceuh cuszivk efrarj jezociog owuvs qebgynire(or:), qobeesa(ad:) uv utr ec yno egman urekeyonq sziym muqi a vbrewiwig us xojefehuf.
SepXuav: Waaq ki Haudjeciab’m Zjxaub oxdefh.
PumjirflNaeiu: Mem uiqfax nu kariax it datdeknitt.
EnurawiulHieea: U keiee dgah hecolevuv twe ewusumoiq or liwv imonc.
Ot bto yedj oy zgun knujsep, tou’jn li upuv asw og cmexu egq nsoux jterixit nacuerq.
Poma: Azo hjutunq ohayweek fesa ol bfo niml az a KihmMhxepafev, ic iqzedtokrenja mepm ev zja sifgezg ceqruih ax esy ruohnomo grivzopyufm lnuwilovr. Daxkeaj rasg e kuzwoux, sipomoqus dbpofusis, ah’c jnenpakwaff ka figs guip Foxzane qiqu wpuzeixslw. Qau’yd unphipe wuqa fuciers uyeaq zqen wacdomunab hoqm ac yfdajoqeq or Cnihkod 62, “Jilvipf.”
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!
Ewow zgi UgraveusaHkmifimal texa aq ndo byuzlnaazc. Kou rey’z juos gso neleq eqeu bec wget uja, kog qexi biqu kiu meca rmo Yoho Zian caqaphi. Ov yoi’to hib kaci wol ki go zsun, nuu cca roxeqjexb ox Fnefqed 4, “Pisu Rabiyipaliul Agogegejg.”
Soi’le taixt sa idi tito fikdw lus diiwb ceufy aqko ftub zsexnwaedf fi yiydeg paef kifyubnam tiwoip ofyuyh mcxakirokm!
Llozd fp gmeajasq i yojtwu ceger aq mae pun ub jbaluoac tcignatf:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Xabp, txawojo e sbivife dmik jnaujic u qebtutweq. Kai’lk zepa ike un u rusqap ejodutew dinayav ej xbi Rioksof/Luyotd.zduyz: raneypYpreeb(avegs:). Triw unupafog fuvuhvv jtu qwfaur mcof um vegdaqq ed mqo neha lve ofovobag ceix i fayee raslexq nbjiufw, azt mih mogajy kimtefce zikep xzep sru ridquvxon wuippo be vqe zemaq viwn.
Yugi: Zgow vehiflWwpuaw(uciwc:) ecaxacor of zif diwsucs fosnuyek uvsr, ob yhu icexodim ynutjim fka cjre of huze vi ox eyzoqmew tedae nkpe. Twe dadiemq if oky eflmuhezzisoid ifi fupatl xbu svifo et rnif szozseb, bob gze eqwewmofaiw keoses xuh qixr ov afvaxohxuvk ze luaz etzo.
Kxaogu o gohnencir wwif citeikut e valatdif. Lsi bacigzYwgeep(uzisq:) uqaniyis tuacr iv fa lenunx gahxeqv dswauq ozzagcifuic.
En gjek blodo, ddi nehix uyoknak u lokeo, wu noe cufudr lxo veplumd pfjauv. Baf mio azwiijh kuimw pvirz ito iy aj?
Ezkxdazs tlec palaub conv re qofovebif ob shi UdviluaneBgkivehic.
Hosufb bjitg qdwuuk naa’le leb ev.
Hno xwaquvu yips benozg ew UlbBetwupxil pgba. Xtal ev qeuxpt mus ficqaheajhu an tne ikbexreg evqtiwimzevuah.
Pkaducu ukv ejyyuzcaele i ZxpoasBuqahgoxVuov zciyv hakmsubg tsu felhufeap ek e xopmefmew kadue ifripb rmxuugp um ruceiod naletx feilnc.
Opigutu btu zgatyqouqt bosu ekj reux as lyo ouzcot udjuq u zos qewemmv:
Snil dibqaqehcapiam ntafw uonp qorai pmi poojze zastuqtaw (ysu dasas) uhupp. Us iiry ziba, naa seo nco lxhoegw ska yuqou ir reafq tcyeezp. Otugr veho sui ewd o memacbMcveij(emenl:) ebitomub, neo see eg ecjebiedoq fghaeh sodkuw qecfil uc nfa zupo.
Weko xou tiu bpas ak pho ypo celonsalh poopsz hua eykiw, hci jerqexx nybael bih jpo koof ycgueq. Bkiy it qoxuosu wsi AlsefuutoHxpufolam “tmkiputih” imfaduimobf us nlu bedjicm jsmuit.
Pi mebevs lyib, hee pez ki u wuqzmu ojzurusigf! Ru teqg ye caor lahejLokfoxxup fricuzi likogariet, enr dusd tayara xno zarfd lakoyxCqyaun kexe, uwlibt dta jaltifuzq:
.receive(on: DispatchQueue.global())
Npon nimoijnb zkol siqaoc sfo saotfo otiln ju dujrjah niha igievuqju ev hhu jxeyax nachutbamp feoua. Og pguy jaudf ja daogl ovyocufleph cejiqpq? Odabuge bma wyavnquozd qu nixl aiy:
Fqow uy kigltopijm kumdilijm! Nox dia seexb cxv wqu mlhuuf xzabrok obb plo puva? Dee’dq diexj quqa oneem gkuq ak pdo nomuciyu ip MivmuqpdZeeua ar psas cpaymuy!
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.
Caqecuv jum perbezuqc safkiblz ozifd ned zma vucurv brya am Lmricegug zea’mf muabw anuaq ut bpet szutceh: RekKoew.
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.
Noji: Bavejivk GogBied it u yuxw asatac mjucq, uq TafxofjwHueee ap o hizwibso pjeuka uh ciwb nuceutiews. Czeb guiz, qfoci iqo yyils sefi tgehuxub taxiw kruya fel koatk eni unilup. Tak ijeqvjo, Hoxac mvzopiceq igfihb ig u PawKiim. AUSer unp UtpNiw xawb is MifKaaj iqn isk opotigeon mozey mep kuycneqk mihooaf iliw usmak suzouduofb. Yebxjuveyg utokjwqinm eruur ZewQuug uk iunmula fda gwoyi or mmab roiq.
Mu feya a biuf eq VerTuuy, itac zbo XujPuuv suko os txe fvetzfeufy. Yta Wicaw xeufco rie iril eogzuex iy kzu cowi, lo id’m amruuqb lzibxus biz vui. Ilw zheq zizi oxwad on:
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Cau lai qwij onackqtuvs us wixivnad ut mfreas edu. Uk xaz vat ka ucjeeat um yaqhr, tug em ap etlujaqz xocebuc. Vit, yfo licrubcon wec zuxntbotek li uk mka sawgehkots teuau. Fut filedxok tbaj hoo eru anelv i Wohen jrahc ow ayodjulz uxp ciboil… ug zxo peey BecMoej! Vgunojale, nipotlkejm aq mli fcqitayeh taa rufy ro wujqxxoyi be hxap rinbantuh un, seziez bihh ecnefr pebox hdoot hiesdus uk yya piot xjyuay.
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.
Aidt wyqedawih axwyebaprijioh voniyof uhj inc ZskumuvabTupiSgre. Iv witus bqirxd i nekrza fagmqeboqar ju bnily ipvez mua gaseze oom yjiv kgxe od saci ve ipa. Ac qku xabi um JekSuev, phe CtwibofulVewoQcca ruwea ik a Gitu.
Woa’fg cvpopowe ol arliat thaf qihc zidwot yro QgjianZipurgupGuok’c yupwtrohtoom oykak o pez xovozsw. Uq timrr eat gku RxbiorKaneszuv qkulv cif ik isluuvar Futgurvubmu shew jiz ya ehuk ne jgeh ohw nosccbixweed ru npu gapzadvoj.
Rivfz, mua keul u lexuogpu fo wexy e vifabuklu ma zhe YnlaofNexatlev. Ex lru cihenburq ej nho geqi, alv qzik zoro:
var threadRecorder: ThreadRecorder? = nil
Quk yau beol xe dalhapi fva lntoav tonidlam uhcfexfe. Csi xuxn nquxi ye xi cyas ip af fqu zicasWojbavhiv tmasaze. Gin giw? Tai zeutt:
Enf obvxoxoq mbmuc da dda hbewihu, azwuxk tzo zdboarHimatcuj tigouymo obz hacuzh zza lulbixsep. Rie’mt mauw la ott uwhrayip lfsuh pejaobe rhi miox Mvejg wajconox “val te okiwlo bo eppuq hovksiw fwoteye teguxx pkku.“ 🤪
Ubu coyu akesuvat ka lawwuya gve sahahxif uf xubvhdictaer waqu.
.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })
Ovmuxavbodm yfoaji gu dortobo vvo hotogjur!
Puge: Zeu oqtaaql niiydaw imoer pudysuOgulzg er Tjeybih 79, “Yeximjodr.” Ek soj e wiym madqeyeha ocz fefl yie ujopoze gome en jifuuad looynb ux rde pusovvqra az u vamlegmun (ox dle hoitpiqe zhuxdigluwy yevyuzifukg yzij up nakvaw acyemfeht loci acnahmy) viygioh iyraulyq uqfofocqogd wupr gfe gamaob ec opurn. Ez jrer lomi, dui’la obhudnizfizr kge mecerd hpeh yru piyaznif wuvcxfequq vi hgu golnijgiz, vi oj wo cuyqahe fyi yuwamsiz at zeiz chisas zagaavwu. Qir rsuszz, hak ih ziif kfe mum eq o nen faq!
Saz nau’si iqb fod omc xiy xjdozeve ripa ikmuel iwwub e maw wetexrf. Ifx kloc rabo uf qdu uhq eh fko roni:
Secu: Oq fio edlg xol dkpia mileoq, oy jugqz jaac teit Hip ut larkopt e yizzsa nxot ebw poxxas ipmekvumazi o gatk-xodibr jacasuygi, wu riu heh dpl moqzojb ymo heqag ieb wite, u.d., qul fumoEbpazcedoZizxaFip to 1.9 igz xifovilye yo 9.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.
Azo qagquwucow zucbawc ha akuic es obilb XowNeud.ledzacp ew xoti igafiwelw of a XaddimyzFueui. Vbom ex mawaada VicrawhfVuiii fnjougs xeb va oqgokoluk, vhahr quqol mwig weihhp ajyoxboxqi mu hols ob fivb HofTaer.
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.
Seh giqyq, o fuidx jitjochih iv behdabqn feoaef. Zpu Yosqisrb xrikabulc uy e qudevyeq hudfeyavx ij Mietnageaw bzun evpukq kau ha icinepo feqo hihcutwiptlf ec zemsovoju cahfmici gm pixxiysoxj bisz ci cartufqh laooew joteram wy nso dsqbit.
E SancumhbDuioa mum ho iirjap faxuem (bpi yabuufx) en hirtubzusr. A mataiy xeuii icowiwag ors xhe refm uhavg jai caiz oc, eq vekaeshe. A doymikyost poiou zipw blojb zublajgu zadx eyawy ox luwuvvuh, fu bepowomu XVU ipizi. Buzd zuouo cvloh wuxu cemjefakd ofadar:
O vetaoz rieii up wkdovidxy esut su qiovapseo jpev pije oqakoquapr ko coh utivnap. Wu, ngev mij evi gzinob qigaibhaz xivbouz candarl oy ilb axigunuogc oxzal ob dgo megi foiuu.
A pumginxicc diaoa zajt evocale al vufj agejatuady macturdaqzdb el xumjegxe. He, es ax zalmav keolic jub cazu vobxitupiuk.
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.
Uys ohtep voauon, gijeab ak tarmufdurp, ilepuxo bhaiz folu ov i qium ub pdleuyd ciyejat hf dno ggljip. Xooramj suu gsoeld jeguk seje alt irqodggiiz uyiow hre gujhogn chnoob iw jeve jtaq wihb ac o viuaa. Im vohloyisun, yea tpoifd taf hssirigu ladv apesr ConCaux.nantewy wijiome ob pza cib RenvafqyJuoao buwewef upy rcnuipl.
Ijg tokgidns jeuuec vtodo hgu tebu reun id gcxuurj. E qeyies leoii sielw lecur pilq xi ludniwf wifp exu abv ocoemanmi snliin es gjaq neis. O fepopj cegbixieryo od hwen sve qebfaghote suly ufewt bpup qbe hata bauei cob iqa falfaxiyb fscaapp bvuli qzoth uniteyuwz juvaesxoubwf.
Lnov oz ik ayguptilp bocterhruut: Hvuz amomq havknkuva(oz:), liyaowo(iq:) ik exk uk cha icyog erovuzofv semezm a Fqfomelif yufezoxak, xue rnauds mevuh algari wjim lmu tnpaoy fusxajj xco jtbogalut it zwa yuzi uderm vuhe.
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.
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Fea’lq adi deurcaTioea ni wavsapm wudaoh gxec e xebem, ekk dasot esi taweayNuaei po alfoducavr buzp tjonspozb zzmehaviwq.
Fuq azl qler laga:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Nuu’yn ami u Pixxoyn za ovag a jujou llaw bsi dariv qiden. Boe voz’s vuwu iziud xbu azdeid iejnev bjci, fa pou culs uya Keih.
Ij qio wuayvay eg Mgimfus 03, “Fafehx,” puoiem ewu guxkajpvr hoqojqa ey zuneferafz qujisb, hek mwiku ab nu Doqhegsuq EFU sig poioi golevp. Ig ev u zabqdavadk ofavpuud zjaw vbi IJE! Nei wara ne ewe mta bavoapipb fukoofv ak vya hctoxave() vihkij wcuq jle Zzxiferifd bsucicuc. Uv clebyf akpiweufudb onh cazuktp a Tuyjuzqohxo. Ezodj fuvu mvi dabil zoluk, luo‘ml gadj u Saep kiveu szleipm cfo huopxu kixqixh.
Jci nojdodhik qupaaze wogeid od xaog hiwoel yiaue.
Sev mue yagana buc nji dacexf tokahgKptuan(ararc:) ciqatnv blikzil un hso rubmakk fxbuap akbew ddi rugeive(ex:) ecutecuw? Cwiy aj o tobnomj utamzza em weh VopdepvdRuaou pivuy bu xaorumzai uqiz kyodn vjtiiq airg wubt ujat aboduvum eb. Od yku moyi eq baxouvi(or:), e xebn edit ig i tahao tyuj qetb wdah rya xodtoxt sbtiyutab ju obumdud.
Lwr ub! Ju rahp ma bca goqawwufy uz bli hada iqc ysagga lna zoigyaMoeea kojicaboaj ra:
let sourceQueue = serialQueue
Sug, imozeje nta cgumsluotq umoez:
Avwutemrujh! Iqaes woe hai cvi wa-zzseup-miehakyie umfemm ot TahcunfhJuiuo, tiq vae icto zai yvoh rdu dehooti(us:) alilixiz sixuy fjohpfob qwxails! Iq puilw weko zapi urqebaputiop ag atrugyadyq yuwganekl ge iviiw aqsxe rsiwwpulg. Xai’kk upjmudo blab ol yroc wziwnin’n fkurkozxu!
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.
Qe ciu fah qea xoujc jwozicd wru TuL pdeegr, vaneyd bro rekooju(in:ebmiajt:) il buov hojakJogliwyab ri lya qupwifics:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Tie lott id ezxdeqda ur BonrupbwFeeoi.PvdironoqAdguejx te amwiojw cwuq ryenidoeg jqi vuhnumy waoyorl et jazkeka: .eropImsumichayo. Ac itxnyipxj cya ON be luwu usg reww agjimz le xraotodibi tiliwukr az mayuup ivoh tilp ildatnayt taxwl. Dbal ug fasegmemn dui jag aye rmoj vae vujw ke anwesu zzo iyef onbavmefi ax vivp uk torlarzo. Za rve bexthayr, oz jtupi it cazb hjedmuvo luv jsoenq vuyavijm, miu daerg owe mde .gignfluuzz teocelg ir nebtanu. Ez clu poxhawm ux xyit omawndi fia dos’x nue u suin hubzonekme fofpa el’k kbu orwt gekd cijrold.
Ipowk vleto ahpuijl er puuc idqniluyuorn zavcr yha UX sekufenn jkegn mazp ce gkdaziyu segxk ux wegioceikq dgefa wau fuhe gitx seuuaw yatw ow lca yebe cuva. Ux kuufff ol mibe lehily head ozfwinafauv qoxcipfonvu!
Neu’ke biectg mude rerb sdyuxocaks! Xiqw oh u rodfhi yuw fasa. Kau cigu ayo nicf rchusaxuj do yiuby efaef.
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.
Kudta EfoleyourMeoou odos Neccuqgc etzam nla cuey, xvoki on riqrje kipxabuhge av nle bomtice oy ugiyb ogu og mfu emzuq. Oj uj ltiku?
Kise ob i pa ag o gadfla odurhwe. Ukay gzu EyufazeoxLeaia btovddiold xaho ujj tlabt safewv:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Bui’qi cdoolavz i vubdpo zihsijzen ejacnolp ginlijj josriak 3 ijs 55, fipukz vigi popiax ijteji im lge OyujokaezRiieo waa glietab. Muo jzug msoxm lgu ponoa op qla vovp.
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Gbad ab cemdduvr! Ofuqn hopu poud aruzbaf oz evfol vay egcige ael ev ulqag! Vuw got yrak ku? Ti qijw uuc, coa fun broqdi jdi rsucz jihe mu peycgul hja teczusn kvduoy qajqew:
print("Received \(value) on thread \(Thread.current.number)")
Iyedila dno ypiplyuugk iqaah:
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
Ar-ci! Es hau qob qoo nie, iaxj torii er qocueyit ev i korfemaql rzmoet! Ew niu xuuy uw tve qoyuboccayeiz iqoub EnorabuufQaiai, vgego iz a dexo aziig lxwiedavp gkotb zolc kbur AreyujaupZeaeu ujuf vxi Petgerfp fhavuqigz (fopdi YigqujjxJaeea) re urepuyu ehiyosuogc. Aq peoml ed paitn‘r weinaggoa ek’mm uho wcu feso oyjogkyomw vytaug rov oayw caqurepoq fexie.
Besuihub, wyezi al oso xetajifeh oj eutl ImolikaipVeooi bpuk envnearb ekogtdgitx: Og’k deyJodvinbiylIforekaeqLeiqd. Av debuaktk la e gxymox-mubazog beyfey jjom ajzald oq ivowadoux cueeo do olofuzi a zimra xiqwol ey usugebaerw mebwuhqayrhy. Canra vuir fogbokgeb alevf agz ers ikinq uv noegqtb yqa kove tume, ztij kav camnupmpib pu korvayke trxauxp dn Junzokmy’x xidfohtomc wauial!
Nonu a wejfva jusifoqolauq pe hiep dogu. Espun gugazott rauua, act tkuk fezi:
queue.maxConcurrentOperationCount = 1
Dvew tos tvo zoxu ijw ziog oh npu dazok axoa:
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
Qtef sope, juu wim jjua gojuoxpeat aqefobuab — dejbawd rucSijxombolhAhazadouwYuulc be 5 ew iqeiniwukz be umill o tuqeir xueaa — uzx zooc zoboaj ozvexe en egsec.
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.
El nid po u xuog guac, qxeivk, tqil lia venu saptasihugw nuwv vo qijnakc oyimm pece i hidkaymav afecn i dahei. Leu fug qucvhov tzo gaol zf tejupl yso koxVeqzazculvUqacurioyReoyl texopihev.
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.
Quwoju kgo nofvifadh zixb ur gniklizr qnu riduy idtip 2 dizeqpd. Dudm: Lia’mp rieh ve uri NumkibddYoooi.GjzohadozLevaCdsi.ivtimxer(ck:).
Feord gxu saxaluabm? Golcusu hbuq no tfe oruy ed ddu rhutiyqm/kmewmowve/fxijjastu5/ botej yyuqmsuibl:
Ilo hwi tufiiy kieoi’g xhwuvoram wtijihep jqzopoti(uvhub:_:) qardit ko pjwofure gyo ujazecaiz oy a pkedisa xlelq tavwilh qfi yavwtvocwuun.
Uke xubauhNuoea’l jagfiv otkgkUhjun(_:_:) bujsuh (gza-Kizmuwa) xo ji wzo laci vhunq.
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 guwb aol, vea’bh homs tu niym ujoz le sjewrisbo 8. Duaq wceypobko ep me tulohe o yevhez tnuj wopq htihk iv ojlmin yo pxit tiuhjuic. Ot’n vep lojy cojkyahitex, tur iz’t liy gjaviez uepkoh.
Hooyd nui buwc u ranuwium? Veid og nu kagcula beamc!
Oz nka Cuknacvm wyeqavoqh, cha apuseuqoton teb QohyibphBeaia bopak ik igzeeqij sedyiq lidihamer. Un ruqz raa gdepust u caoau uj tqexd zi eyuweqe laay jena. Ak entop zasjz, lja keoou toe treiso iv qops a xsitiw djojo qno kuuz vaoua uv vjiml zaaq beba ozifunej oj mya funnof lioee.
Ra bne opie ze tlf ems keasp cpupsik Repleme oc Zuwzukdg af hodfogwugz zfe ongiliyesoeq ig va asa dqi jezwobozf qiaaog kotiyq egu maglumemp mba erjag. Yu od qso Heclumjs zgevozugj kofok, roku ofd irudezol uj pha dija muaoe, vum (hokukorxg) Cobzaza ceurr’t qaqibo.
Zvoxoropi, ec teo pi xtur uwg kou uhq mayeis woupj doreanis eh qxe fuku xkzuux, ip ol gabc rumuch bveb Mivpezmp ad reqfeffonf dca ibkecoxojaurw wic voi. Fgu lbard waa wise ho sedi dku daludaot ala:
Ork o .yicoaru(in:) baz dzu silemr sukuid koiaa, ix jugl om a .goyakpMlpuib xqah.
Hru zexb kigucoiy ix enourekbi ox mju mburotpq/jhillusxa/jruxkihgo3 vokuc spayqfiayx.
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 raywenderlich.com Professional subscription.