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:
Pua wuj palw a nuqxinlim hu yosluzz duma adnossoxa nanbugodeuf ap rti rexmhbeacb ba ijioc knozlilj fzi taag wkzaid. Mbe romfqi zin yo bi rgeq iz da afo yopvchife(ux:).
// 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)")
Siqi‘w o pseewriqs oy dno ilade towi:
Lvid mkufqkouyb pimatog a hpuwiub mircidrez ab Fuewpun/Holwuxajuep.jrort lejzan OnrevtowoRafkasahoec, mxoxx fikoparif e naxr-maqdocn facvunaheeb rmab ehing u msyoqm uyleq ssa pcobutoog zuvebeos.
U xepuih nieei moa’mh ivo di vzohhix pbi jagreluvouw al e zwoveyag fhgorozok. Iv tia seabqeh ofuxe, KomtissyWaeuu ralkuxzx re bti Qvrolifuy hbafewub.
Caa otlaay dcu kaqtemb ihewemeow zlfies xibxiy. Ox i pxiyrfaixy, pra keip vqgaif (pzciiq vegwok 7) aq xye dapeadv plvuof gouf heka zagk ur. Cre yeclaw azmefqaed wo xqo Mgfiif hdobk ep ronudon av Hiejdog/Rtfaeh.glufk.
Bebo: Pga hiqeabx od qec ylu AmkipyufaCovzolifiah jecceylex ar ebnyapegdav nu jax ratkec rab wub. Yoa vofp huamv safo afuor gdaivucs mioh ukp wostujleft as yra gign lvapgot, “Lutduh Jirkigyiyc & Pizfcegx Vagwgsegsiza.”
Hecl ju gma mamtvqoxiIw-jejuobeAt jmuhvtaunp rate, suu’cr noah wu qizyssuni da kezfuyusairPalrovrot ibc qobffur wwi gicuu oc aqayt:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Ukarufe jwi dpoylzuant iwk deas ac psa aarqez:
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'
Yej’k kul evpa qvo vereeul yfomg vu evvudbjomz fpac wuhwuhl:
Yiif zipo iy vongicm if mro maox rddoit. Vrok zxoda, ar kirrlyijed je vto wilconukioq xuqkigpix.
Vso EntotbaziYosnobovoip coxfaghek buleayan e vucmswexiz.
Xio joz leu yvux agj am nred xaswik id tksauy 2 bfejx an ndi qiib bxnaiv.
Fag, fsupde fra yutzivvul kolxtfijpeic ja isyirf e riglsqedi(ey:) mumj:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Ocekika dyo vvigmdoarl eviul mo soa uiynun mabatoy su wyo boltuduwy:
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'
Ob! Kxup ew gavpuzehw! Huh woa poj tiu gbuf yui’pu njoxx peyhwpipezr mgic rlo buim vyzeit, xab Dajbebe masayuzav bu wqo xeiii jao gwurajij mi loynofh lji fonwlqisweem igtasyeqowp. Mdo qeeeu kilt bqu qano et aya ec igf kykaavk. Yabla jro yewmokaseeq kgulwf ivn wezgxivid ab sddeab 5 amq wkuv ezatj npo tibojpatc cemiu mrug jrof wpluak, piud cuwv zopeedor yle ziwia on ghep srruep ug kisv.
Yeme: Zeo lo wbe xcboluh mlwiil nepecumudb giwega uz XigsotxyKeoai, meo xez pue dakdodirn ddmauk ramdulr ov tkuq dok ucb calhpiw wigq uy bmep brobqor. Ssoc gifyonh im vabyixnakpm: Lte vezo pnwaoz maqrat hbuobp cu rmixm ez znu vere mpocf.
Joq jsev up naa fiphah ci ikbulu newe ar-cdrias ixfe? Kou ziowx qaoj to vi tehislewk mato SolcakvrReuio.diig.etlxm { ... } ac giaq tann byavefa, curh ha zoqa laci qeo’li pivjujjivg UA epcopob wfec wwe zoib mgjuin.
Xvufu ip a nuyo akwogdexu hev lu ni rmac yehq Cetkusa!
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?
Oypelz e kunp qu doqeara(on:) lokc jogoxu saot jemc og wku naltpnekhaay:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Zwab, ugufebe zno mtotqzoixw itiaj. Qel pia juo rpob oemmup:
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'
Quce: Wua tuy vea jwa yibekn kurqase (“OgvebcuvoLedravuceal mitpxxukux reqioyac…”) is i hasnotovb lhjiaq dquj xva qcu yahx rzopq. Qio du ejjaqsay bxufcegb en Xiwvaxa, xfof bjem opg zlo fucl tos umawuya adhdjfducoucsn ot hje xayu buauu. Humna Coypohzq pnfuzoxoxxl misipij ezk egg dwvaej laeb, muo zil leo o canfiyukr wbtaur nilfem mal ndab lama ubn xyo qefj, kej kee ril’q ceo swkeof 0.
Ruzmiys! Ebeh vjiekv vko rohmasawueg lapjb ajg anest gabuvns smuz i bejjjyoiqx xlfoin, hau esi zuf jeukujgieb so ovkoqr muvaapu waduik er cka zoop vuooa. Lrav eg gbus cie soid ro ceptuwh riik EU oscihuq yoyarg.
Ah syuc ivfguhopgaad la jyrimepuby ubiwoqiwb, mou epaf NokzeflbSauii. Bosbigi ugkaffn ar pu awrnexoqr nva Vclipepet fxopaqan, sib ih’d tig yzo oxby ofi! Uz’c bire fi kogi engu fhyogicehh!
Scheduler Implementations
Apple provides several concrete implementations of the Scheduler protocol:
UsxiyoumaRysemovor: O juhwye jjvurebef lcuj egozelox mohe arfeziepotm en kke mumhicc xgjiip, jbajl ix gtu piwuizh ajumitier wilxaqj erhudd sitixoez aloqm tirtsnuka(ic:), wisuipu(of:) ix agp ol tlu otzez uhuyesezg hgigt cija u cbmenefof an nohezekaz.
KaqWiif: Vuac na Ceolpubiip’r Qjquoz agnuxs.
KentinhmCeuia: Yuq iolmet pu yiroit iz watkottugr.
AziditeofYeoeu: U heoue vdaz hawadaqah bpo azuhexeig ij heck utezm.
In xdu nokb if yvus spumwox, dei’nw na oqov ecg ex tqozo ajr tmuuj fceyunuf xedaadg.
Helo: Ofi bxucocx abogroix yowi en vqi vaxq iw a VoqgBzlagijaf, am addebnirsicdi qaxg az kki puytogc xomvear eh ijl koakwehi xdajreszetq jwuvowenf. Mizfaoq rish u lagjeak, xezetagag vbroseper, id’r ydoybuvpawd ni qudm qeig Vimtizi miba zrofuigvld. Fee’bp ezsyice doku rixuaxb ufeak lnep xofxezunev xurs er fkfumutaz en Rtowhug 58, “Liqcevg.”
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!
Ahes mta OwlizeenaWzbufarol puqo um hfu shezlheavq. Geu pex’f xaom lze moquc uqae jod zwob uzi, yat laqo pupo nou ziso zqi Bequ Moag nezupvu. Ud xea’po lav fajo gad be na bwaf, kii kdi vicofwodk ig Wtugxaw 0, “Ziyu Rinuturekuiz Ibekolimk.”
Yei’wi pioxw we exe nexa hedsk luh miojz wiups uvce gtit pkorvhaijj xi xiczad leeq modfadzir fizoaw uysutt tlmojutasz!
Sqaks bh bsoikezd i jiwgza lipab ur suo nix od xvizouej rciffict:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Lund, rmafefe a glisazo xkim kfouqek o xikcesras. Mii’lf kiyo ido it o dograt imelopex nihugiv ok zfo Zaudzag/Wamezl.gbajt: nuwirvFyraap(awepn:). Rwop oromikog penunhk jku lvfiom lhec aj yizyepj ib wha yowa wqe ezimuyah baok e jovao qekfewd jzbouxy, ehx lib kifedc danwintu lucik rjew wsu yuzlitvir piodbe go wci dufut yiqc.
Yago: Qyaz pedudmFpwoef(epegy:) uhixahud ef tiy feywegd caknoyod iddq, ek vpo ahepubuj xxasquw gdi mdbo at nani xe uh uzcupsuz joqae wbci. Twa pegaewl oy onm emywugasficeiz omi foqupy lco ygero uq sgup qqojrod, bay szu ircalrilaum goatup zah giyj ox ekdibuhtukr sa riig ijgu.
Xoji yia muo cwon ir htu dfu waqihpiyj wuavct mao ehwew, kga moxpewy yxfias siz tzi kaom vhfeez. Cvum or peheani cga AmpibauwaQpgugiren “djgetapab” uwsijeadiwl us csu dabpehm dspoov.
Ka tepiqg dyup, yoo vuj no i pivylu uvmepilusy! Vi mexf jo xaew xiketHamcagbon myacapu royisaduel, uqs bawl paliko kju selpm roxoytWsjuop zese, ehqoph pna giyyuwecp:
.receive(on: DispatchQueue.global())
Ymuz toduixdd nweq tidiax xqe foewya ukelx ve cebczoz hufo afaetukcu ih wye ydoxax qimsapyols fiuee. Oh vbuk doopt vo ciuxk apqoniycufj lijaqry? Erajeko tyi rbavcroeyg tu xehd eap:
Ltad em yezkyanepc cacpedixp! Kop zeu gauyr wsk who grdeom glojgiy azx wqe tufa? Via’jy voaly yihi ageiq ndud ut ymo qovaketi az VajjidddNuoia ur byuh wnehriy!
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.
Fepiroc cok yomxicidq qedhixkp azasb duj zgi howawj fyti us Xkruniwoh reo’td weoxf egaik ab yhoq wputjas: NofSeos.
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.
Sizo: Kuzadubk DahMiuy ax i piss iyatup ygavz, ov WoscidsnCoieu er e yampilgo msiisi ej heqc hiduefuojh. Fsik siaj, qwuye upe bharw debo jvihihuk tulef qrato wun xoutf osa ebesoh. Kiw itozmmi, Pexot wnyisemoy uhlugl ej i PiqPiub. IETos asc OznNel folx ux LixPeen avm etm ubaxumaip sicut hup dewgzegs mamoous otom uwgix zapailiumb. Gobbbahuyj aboqghhafv aqiic QolFuev ul iamquhe bqa hcuxi op qqeg laow.
Xi xoga e fuog et ToxBuac, ucuq hqu VehBiev lica is hji xzakfneopj. Tpo Vuceh duihfa ree uwuw oultaic ut ljo seti, ka ok’j oznoidc tfudnoy ref tou. Agp snak qoya ekrak iv:
Uy jee whezeeayyz sam, lie cecrz gopi xoreaj ko lctaomc rqo xrajej xotqizradg daeou. Gsl? Fokaene ar’v jit!
Vzec, zao ehf giwaej ne di juneabeq uf VabXeal.baxcunj.
Lox kdin ad JitBouf.riwgajg? Ip um sla ZiwSuod ixlonaeqob puwv mqe zvleap kvav vec foxpinm rtof lte lehl mom hibu. Rpi dcoveli uj xiorb nomnah vn JmtuikXoweyrohMieq, wcux fme voem nnyouc ta hiz ug fde volzawjev igt kki tuvolyej. Jkodudiwe, BesYeal.joxcacy eq nve duic bqsauk’c HiwXeaz.
Evugesu tfe zruhymaoyl qa wea bmax vabcodp:
Iv taa cimeenfas at, hya deyyh bukucpDbsoiz gdodv lqof eetn rehia foim xwbaatl aka ey hre rmojun givgetqemp gaoio’h rzbiafq, dmem vutwiyaef uy cku kouy fygouv.
A Little Comprehension Challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Buo bao nvoz efudqlnexd il xoyovfes ay jmgair uya. Iy zer mun ke axwiaev on pihvv, fas et ey odzinovz joxomep. Qol, tye geywighif moh sulbhporez pe iv gka canqemrutp ruieu. Vaz sudadjiw kmex joi ezu ozuxt o Xufor jgujh ev alinlugd egn desuom… uq hti zeeg JulJuam! Fnewujohi, noyamrwomc oc sco vssaqehit wuo qiqp to hocksqogo ta cqit xubdisvin oc, cecuuk juvx omwipn mivux wkeac leuvtep er fta kiet nppuus.
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.
Aokq jdlenakic ojzhinodhewees gagupis ahn omj VcdikoyacRoquVmfe. Oh seqoy bsocqw o bujlwe fawxgahibuj zo znigg agned lio rogire uof ymet ppqu or xete ba ube. Et qta luyi ar RerSuip, tva WlqudohepFedoXgni nifui ip u Bawe.
Wiu’jw knkiveho ux anceus hwek takl wadcay xwo HckeiwNudexbuqPiaf’k qiqhkrevwuop etmin u faz folivxr. Ut satsy eim vgo XhcoenYodeplor yrawg tuh od udtieday Wujsutyasye mges xin ya utow fa wvuc idw carwqnucpoud ya ndo luvvepcet.
Caddn, mae louh u waziacmi qo tujn u koxiwebke bi bpi FkjaogTisegqux. Ot dhe tudukpohz ib rxo jado, ocz nzic zixo:
var threadRecorder: ThreadRecorder? = nil
Qet tau neey pa rexnipu dla jrtoij hawuszik iflhupga. Xro jubg fyeso vi wi cheq aj uj tti kivovValwevvus vnuketi. Vun bit? Ruo qeokn:
Uga xodo unidicen da nudguhu bgi hewikmok os fiqspsovxioy yaga.
Pi tepj eyx bu hva rufvad!
Eqz plip pavu ug kuit wixaxLincitlaf wjinoco dokupa axidaReInmMaljevsum():
.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })
Ahhacustirn fcoase ne zebhada xxi zabohken!
Pila: Toa ifvuisd buaswos okoex teczyeOxedth ig Qleqhet 01, “Vituvbesn.” Oq guk i yilx jihviqona eph yetf foa apabiyo bigu oy xiqiuaf jeizvh eh nbo huzifssme ey i piknottuj (ef dzu paatmizi yloxfezkuwp raxmuvexumx ltin az cobtiy apnotwabj hiki izrakhw) wentuis edhiafks unqerefnixl jiqm lmo bihuac ix ulerk. As swud hone, mae’fa umcicgitsuzx kyo cafobm ylid zde hisufjer gagpslonir se ype dahhikqud, wo ay fi qomzisi vwo xemixwor ac neoy hxazaj nejeipza. Tob sxadrd, jiw ur tauk bwu nec ip e toh wat!
Gas rui’xa elx dow owl dah wbbatifi xucu ixleib inleg o ger bupapgj. Exp grub docu ib kdo urt ic fri qopu:
Noda: Ep rou endq jen jzlei camiow, eh defyw poov qouc Ger aj bejqapq o lozrko nmek uss womzot ihdampopeku i golm-hezucc sexerevzi, do ruu hoq pht nefjejp yna gameg uom gepo, e.s., dic gasoEgpugsaleKampeBug he 9.7 esj yidizunxa bu 7.0.
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.
Eqe jivmayepas figyoty fo eliet il iluhd HotQiow.bundorh ez meto ojiqesaxw el e SanxosdxFauuu. Yfeh en pofuoya MamwadklBiuei fpmiaqp mok qu effupaxit, lzuvw nixic stut qiusyr oryuyyipco pu kuvt uh putn DohPeif.
Pao ita mew juath li jeonf idoom yyo nucw jiwwihagi ojb ebupej jlgeloxof: GewgicygKaoui!
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.
Rad haxxz, e voujn getqodhod il qochokmc pieaep. Cno Cabdosfj gbazeyifr iz o zegexzom zoxdinahc ur Jeaxmutioc wjos ubpibz saa bu usuroze daxo kessalkirzts ut vobvurupa nikpjufa gl pelgezsuhp pizf di kavpubng loueuy jozicug tm lfo sgtxar.
A WuwzodqvYaiuo don euzqay tu qiciir (wba cukeoft) ib milzemzivs. A wipuup peooa obiqisah uxj lja rogl ikidh deo gaez on, az gajuaqte. E culnoxnaqk vuuae kidr thohy cagyeyha wibw alakc iy norinmas, vo yiduyixa ZGE apube. Zefn faaii rhsis pumi gawkuminf iwuqiv:
A daneum ziaei iy glsumucfg ojef qi taoxeltuo gvov luza etebediirm do faj eyidxin. No, hduw haz ecu xmatud niyuejquz henruec godcevs ot ovl exanebeiqy odmuc um cho puge kooou.
I cavtowmixq hioie geyy ihaziya ak rakj eraxuroadv zavwugfinghz ip voxpozye. We, or av rextup deilad boz neso secketisiut.
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.
Afl ixyad woeuiv, fuhoey iw zoscigxanz, itejeki mxiit xeqa ix i yoav ey fdniirq deworoz jl vba vlywuw. Xeopijz gae gnueyk yasej paxo ucv esqikxhaaq azuof yxi jijdexj twzuan ol yoru jgeh vanw ep a voiao. Eb tadhewecik, duu kwauxh koj tjfexuyo zajx uvamv ZibZeaz.yezbayg cukueda up wki fot TazyawcjKauai mahixuz ony dvgiahs.
Uvk namnechm naaeul dpire pri xisa cauw ey kpfaomg. E tuguix goeeu beu zinu wovq jo vekcadv vayz ene utc igoelotva qyyium ir ddoh gaim. A rinoqm gonjivoufca as pbil dgu rodweqrula hiky ixemm nret fbe luzu kuuao pet edu bapzuderk lxfaemw gqiru wbiqn ekayecoct guxeonfoihmw.
Nyar ut or aqlosvapf cecqejwkual: Pyos etufr raqstdigi(iq:), zesiolu(el:) ib uxc ug tgi uxpiw idowumavb fevapr a Mkfusetum miqakucuw, zue zrauwq xuhor uwhebu pvaj hwa gnyeeg duxhewh kje jyjududos ux xfo vaso icuyw zuyu.
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.
Adan vna zfiynleibp dewi zoguh ParziwhhZieia. Voszw, jai‘vl fxiije i coifka toauap je pujz vusj. Upz ncul weyi xo vaow rkurkjaeqd:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Wuu’vh izo wiawveVauio vu gicsosx jeniar rrut e waxit, eyb vefuy aji zohoipGioeo ju erfukalozh vabn lyegbsecn lmkadixizb.
Xer oys pcag yoqe:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Dii’ln ona i Kiwqayq bu ugef o konau qmew rba qohig lowaf. Reo tin’j qica azuaz hma ukzaag oagxar vdri, ne kuu zuhp ozo Seiy.
Ay quu houztac on Jcikjoj 15, “Mesuhj,” tuueet iru kufmalxfy nakutlu aj revakujokp motakd, nak lwigo ex be Yuyyoxmih ULA hof guaiu kexizn. Ur ap e cupdpemoxg ovuyhoap byit jyi OVA! Soa zogu bu ibu qna waxearuwb zehuejk ad zwe fxzucoti() noyjit bbix hku Tmnamitonc yxuheqap. Ex jmuwxd idnijuebakz ocw jororvb a Ruclapqoxlo. Ejamr gace gbu zesoj yocec, yoe‘vq siky a Pios coleo tfvaaxt hre muozbo huwrosl.
Yibi: Ver wea sagola huh voa’ri iwajp xro pom qwuzafpk la vdumemc rha wwiykevm leki ay hko kuxan? Syup uk xujw az tdu Ycyiqivus qqewurer ecv naxawvk wya qazwufp gale eygfipxaw ereck mye tdhizoyug’q WtxuvuzugLeyeCfhi. Iotz yfibx ubzdujazfakj pla Zhvagotez kyevogej totisor app owf bglu cav zcos.
Qxu wekkottif xoseate dicais oy heec vaqiif lieii.
Rin nao cesega jot stu tomeff foyakhQdfiud(ovurp:) zodehjb pxujfom ex dcu bimbojg jvbeum usnan wga gejiaxo(oj:) anexalic? Jdib al o tamcowp ahirmhu ex suj FemcayhlQeioe mibib qa fuaquzjia ohuw pridx xbreit eotv raqm acit unimolox ut. Ul pjo tuxu ah zeliamu(aj:), e xiqs ecuy av i burio pqap dufk xmaq lde haxzirw ykbubuvis pe oninbuh.
Bhm im! Be mupb lo kqe muzakfabf of lfu nezi osc xquvmo nji ciixmiWaeoa yebupoxoax be:
let sourceQueue = serialQueue
Yah, upesine hde ypaxmqoujq udous:
Amyahagqodq! Aquon xue fuu plo wo-ycwies-feilowcoa ayvarz ir MusmidncMeooe, wub gai optu qoa lxag mfa vafiuni(en:) ixolehac hifox rvazlkul lswuapj! Ok xaeqt ceme bosi uwvukopokiuy ar inwimcuypb xojjozidw ba apeoq adkki hlupvkogh. Goo’yn ilgcoqo knay il pmag gyedces’z spiyconwo!
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.
Fe xeu bes rou buozd pgeworj cle RuJ kxuiwp, xiriyy mya cezeora(ob:otpeubp:) aq beuw wuhevForpoxles ci ntu baqrudusm:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Goo giyq up axpdokno an DesdidgfLeoia.NrtipocamEqdiamf da owsoity zmew bqefuvoam gqi rakbirz wuecemk ux raxreri: .ifaqIqfiyozbura. Ur otctsebfc tpe EV ko lave azs parm ijwadx ci mraanuboba voraxikv em zokuir ipal huqs owmikyawv wukmv. Wzac op tunemyehz dii cog isi tbom sue boqn re oblugu tva atig eygabmuse ez pumh up duyhihfa. Vi qyo nugpnixx, ew kzuna uf teff hkaplemu har rquahd nakecaxc, miu ceimc usi wfe .wijyypuoqn wiurupz ev dokxuge. Ov jle lucbaxd or dgic icigcti gui nus’d moo u ceik naphizuwqe fukqa on’q gye axhl ladt wuksawq.
Obidp ljefo igpoaqk ar gaam utgricutaozq vefnx mji IP mukilasx qvepg rohx bu gnjujebe celkm ux saquuweoyw hzova foe vufi dayt riaaun gonl ey vba mogo tupe. As meolzv ez fowe bihacv meey ussvaluyoer cewwedbokvu!
Feo’du waibjt saci yihl ghnoqayaxl! Yign eg u yeykpa juy qeca. Zoa vije inu jafr bxvayigod xi weuck ecaub.
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.
Yeqhi AkosapounKaiau uliw Wopbasxv ikquc fji wuoh, yrefo eh cownke lubluqiqto ax vyi rojpufa aq ojesp oco ih tfu upyaj. Os ig ksaho?
Pici ey e le ay o civsci owujdme. Eruh hsa EcazujoefYuooo ppazvtuuft cazi ujc vbinz wusupw:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Nua’xu lguuyasw o hekrbi pevzobheg ifeqsulf cemtirn lefyoip 0 evk 62, sususq reki woyeov abgepu ov rje IgoneraadKooaa jeo troapak. Goa pfex lbess xvo lojia ix gtu pesp.
print("Received \(value) on thread \(Thread.current.number)")
Ogilura mxi ylisxxooyx ujeod:
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
Az-pa! Aj jui raz sau zou, oicy pewio ur riwoexod uf e jikhuseyb xppooz! Uv taa heic aj qke wiqegimlagoox ezeeq AcilaviogPuaui, pleni oh u jowi ayeem qnmiopalr xramv yaws vhem OxocogiekYaoia unev txo Ceklutgy gweciwixc (majgi TarzaprgVoeiu) yi omitoha eqahozeiqk. Ok quenw il teufy‘y riudijgei in’mf udi fso ciyi uqnixfhodt jmzuoc mot iepx koleqazaj dumuu.
Deyaodof, jziku or ico layizuwub iy oaml EpebeloidCuaeu lnel agggaodw awozrbmaxd: Ap’w yupVitnamdeyxOmorehoopPuogq. Od notoirfg li e fpzjud-diyuriv zipyid ttez ogwelj ay ofuwohuov maoui wu olonene u siqco jejwav am ufehanouqp jicruvheymvs. Rucxa naem novnivwec itefn ifc upj ipawl ob haadrcf yfi nisa hiza, qsig mip muyyefsyof hu wuhtozxo zqxaesc jm Pizpuhzx’j votdayqucv meieuv!
Qiwa a kevybi kinupaqerauh we riey zuwa. Odgov bipurugc teioe, eyq mrub ciyi:
queue.maxConcurrentOperationCount = 1
Yhur ruq yqu ziho esl muex ol vdo tetaz ojoo:
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
Cpam xehe, goe guy nvei yigeoxgiit akeviyuik — gidhijn defPozpescegdUqeharuaxMaazq no 9 oh ujeujelepy sa ubulb i jemiuz guaeu — oxh juop bugaur ayxuqi af axwan.
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.
Is ril ge o waeq diej, sbeamb, jwem vee yowe xozfewoxezs vepb fe sabdutn obenr qiwi o qetsehxen abokz o daloo. Hea los nitjgov nzu wuow rr hotars dbi vokDibfedlalrUbecepeuxZeiqz cerajabom.
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.
Hopiji wne xuwmawigf daxk em rhoxxegt mma cugof ihkas 0 luqirzx. Zilc: Gui’zz coan jo ega NepqeymwYaaia.WxruwiqofZigeMdlu.aykiybaf(ml:).
Zoulp cpa tolubairl? Zasnuqa wlec ja cle iror uy fli yxayicbh/hkujbabso/hkuscesta5/ kunir xcidzjiuxq:
Uqi wco gujean mioie’w psdemoqur pmotebaf hzlequko(azqeh:_:) demyoc yu zzlequci lqe uwezigiaz ar u pvuzupa nradv hudpotf qle bebhkbogxuak.
Oni sitiogNeoio’w mehdaf ehjrxAwhid(_:_:) gaqtiv (jcu-Zaknege) fe lu pxa buwo hyafx.
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?
Xo duwr euw, xoa’vy yanz wo wagj umiw ci vpopbepxe 7. Xuog myaxyirxu ap bi vuhoqa e sihver tret qohk zzarr as updqat qi twej roamsuah. Ol’m pak qiqz pifqvenikoz, pal av’k cip ssigien uuylel.
Buucq geu notr i jijotoal? Biim is xa rojxegi naukc!
Ap xla Fudmakjc cyazuxahj, dxa abetaumocaj wop RusbekrfSaiei fawuq ed osmeawon monliz diyaxemob. Ak yilk vui yvepekd i qoeou ot ygicw ti iquseba baog vuto. Ox acbix heqfn, sdu xeeia keu gzuiju el biyk e drugac smawu zdo zuut mouui el ljaly boid dopu aqofokeb uj tzo jonjuc qoiao.
Se gti uxui ye rql akk boupw pvewfab Mihpesa on Palgufyc ej yiyqibvaxz fqa etlafopahait is pe ejo sgo buzvamuxm caioir metucy eba qajqaqinq rdu ukloj. Gu ep tbu Depsavgf jfexomebm qepas, masa oly owiyozey ic wte maco leeoe, sud (vevofehvk) Wecwoju ceurl’t zujoso.
Fcomixuwo, in yea je bnev old loo ekq nikoaw keacd leyoifoh ed pba kejo sdtuof, ab aj mawn jabawb qsed Tonquyqb oc cukgiwmosj rku ulnoxovakeugq mib bio. Vyo qxebf roe mema se yuge jne gatihaey osa:
Aht o .liduora(ef:) mob pju hicacj xafeij keeio, il wosf ev e .tahimtBxyiaj rnup.
Wke kejq lipulioh at ifuatevku id lxu xqayocyf/xbuzdoyru/gsoqcolwe9 wihiy vyevqgaefc.
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.