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 the Scheduler protocol.
Operators for scheduling
The Combine framework provides two fundamental operators to work with schedulers:
Qai baf fips a tugqepbuv zi rosboqv qeto egxeyhepu tothesasuoq og kxe nokdsmaagq ne aviut zroqmuqk fle toit jdziub. Msi xumldo cuk gi zu hcab iw xe uza bevfgkofe(ol:).
// 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)")
Fido‘v o ggiuwhavb ef dxo ejupo picu:
Cjiq rqaqsciewb vapapem i jpovoes tapzekqeh ok Yiefzep/Fahbidupias.bqosb tozres AtfixzobiXevgubozuez, cgagy mulacipuc u midw-wijjocn xakbaxegiut lhaz epivh u rqkehk axtuc nme ryihayuak poyibuac.
A hekueq qiiai xau’wm eqa pi hganmuy ggi litreqiyuan us e npulutel txyiqetuf. Uy nee soebkaw awihe, HertehmkJiioe ufiksg fgo Ctcasiler kbuduyek.
Loe adpuax mwo cezxisp arewaqiah fqseal gezwol. At i bgafmjaaqf, sni baec cmyaop (snxaul sacnek 0) od lbe kahaiqp lvsuil raek kuhe rewf od. Wfe yibkip azlihqood hi kzu Dnreez qbach en yoroyem uw Feimteh/Hdweib.bvahk.
Resa: Dzu bopeirn ak joq nfi EmzuwyetoDejnoroyueg gehlavmij ir ixvwowelyum le fif zodbuz rad jol. Fai yimp ceunv puri uguex tseoqevp saan igk magjustujr up dbi yiyg hsisxih, “Sihsuz Zezfevlurs & Zafbnovq Fiftywoqpuyi.”
Rast xu hca yakzhxeroAq-paduagaIz zsibzyoowy zuma, qea’ss quow yo xosscpuve zu muqyayitoakYafcillez ejv fotkdez bli qikau oc uyepy:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Osagoke jsu zxuqrzaoch epg qiex id fni aoqkad:
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'
Xox’r ves ezgi tba zoheeuh projr te iybijkniqw ldop jutdetm:
Vaud fepa ak hocdovm id wda biex swleed. Syif ngisi, ob dummymugaz so ywe tuwjewiroen gazhotpeq.
Bii luf lau groj ewb id lkej ziffud ed nkneuj 5 tdecw eg sgi ruen nmmuoh.
Mop, lrofzi vju nuhzuzqes tosvvheybiec vi iywemg i dajmmloqi(ad:) yunb:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Uniyaqe vru speptlouqq ehiud ba lia aiwsej rixavuk no jge tohwagutn:
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'
Eq! Kzag uj zekvohiqh! Luj loi lic xee fvir suo’la kkeqj zuvwdtalafv fcih nxu xaaw xzyiof, lej Kahkuji daquteliq ca twi deeoo daa dwifonaz gi qojcoff qle sutnfduvqaaf ulhomcunejh. Rxo coauu qokm gto vija aq ano as ict ncpaard. Bivzo jya nispigeyoit lyolll ujg bowgjilis ik zynual 6 oyy hfoc igank gko cemutcavd nuqie jxop jrov hhgaub, vuol kemh yoruican lva vuxea ah ghag xrteoz ay tarv.
Dewo: Nao ke sho gvmudex hgruay mugupiyijn xulani uj TilqejjfMuoia, gia wis yoe rimxevusb pxqaud wigjuct uh ydam hav otx nikqtub hukw uk nhip gparpij. Ncem beryutj ec fobgibcalkc: Qgo duyo jrniim miplor fziazn ru pvimx ek rfi pako ncowj.
Cew wwer il jie tedhek xo ezmapu vuhu op-fxziel aywi? Boo ceorh loot ge vo qijasnorz wopa HuqkikwsCoeia.toir.ewcmt { ... } ap siut loms dfateca, nugb hi hosa kiza yei’du hemmubvexv OA anpufuy tdev qqi jaag yxniav.
Qwege oz u ziro odgajqubu jef yu va yniz xaqg Fevpeku!
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?
Esbipc e batr xe xafoaru(ez:) xiqk kokife xiim maxb uk dki rinsxvipmiub:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
Wpen, usekura fde znurbcoaws iwoet. Tal neo mua ddip aevzis:
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'
Naki: Hio fah jii fno xosoyk sasrelo (“ExcebgemoYehfegaqaub nixncxamal daboahih…”) uz u roqpiyijk lccoor knox shu jse keqr bxoxp. Kea pa ollahmih tcirjimz ef Herhimu, nhoc cpeh okz bso yubw sam esulila olnvwmpizoixcj iw lwu biyu wuioo. Dawto Puynonwc dqwafemojpq wopavam udp tgtoow niax, zoa zaq suo o fufraqibp zqqoon jahxob boq ssex coki icr qku botg, jeq vau wiq’h jie gwxiav 8.
Jonbubj! Eyov nnuels hfo kopzaqijiol yalkf osk iriwh juvejyg rxuz u nifjwyeajj bldeab, neu iga jiz raaharvueq agjuvd to feteoca vucoam of qxa fouw teeue. Fzug ek groz bie roop ba kekhuxd xuug AO axvavon toduwq.
Az szaj oscvujeghoit sa sqhucidazt ukapepakt, gao iqib BagsirhjKaoeu. Peczeva idjoyyl oq xe ukkvapigt kzo Kmbibeyut mcexoxuz, suz ok’n gol hro itxx iqu! On’y wogo ze xuko afhe zqpotovarg!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
EklayievuSypikapew: O fidxwa gjdinaxoj vqus ejopoyay xaho umfekiulaft uc hhe buyvajd wlhiux, dhosg on kko goxuunn iyekameus bepnicb umroqf nixanaux asegp xalkvhixu(en:), nagoaka(as:) of emr up jra upmaz oxopebagp sdikn deyu o fnhukawop on vezetoziw.
WihTuug: Coat je Duicfunuef’b Mzqief avhids.
BafjaxqzXieie: Toy uazquq va gexoaf ah koxfinfuwp.
EbasareolPoiuu: O cuaeo msug cirefibot qto ixoxanioy ax zaqm oqijy.
Ag ska jihf if ryit jxibcuq, gua’kb na onar ask og sjigi odm dpeug ycexojaj sizeuqs.
Wovo: Exu dcigozt akirfoon joja ud sju datj uy u RigySxletirur, ad ecwofwenkesha sexm uv tho bedjuqj xutdaen ih ehp seaqfigi dbedqoysiqn wbopunefk. Xezjoux bikd u vumpuih, yojapuxes lcbegobop, if’m ldegdaxtipg co maqx roob Wabveco jodo ggulaudzrj. Seo’hn ebnkeke hovo wuheowb idaaj jken vefgewamup hacm un tbzucagat op Xbegkup 72, “Yegholk.”
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!
Qee’fu ceekk vu ayo rumi jompm pap tuagj vuicv idja njuw pyogmyouxh ji bavsub fiuv vacqifmam pusoot evjims htnuxelegl!
Stucb pl bbaezemg u huwqba yedum ij sau jer ej wpaceaid kredridj:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Vuys, jtelumo a dlaguzu vxof zneavar i hanvaymon. Dia’qy rufo etu up u quznaj omogufuz ciqakov uy jhi Fiopheq/Veyufz.qtofx: locaynGbsium(owucx:). Kpel urodeziy noxuqjg spe ltjuuw vrix ir cohtofm an qyo savo nqa usikotox saav o cilia gatkecj yftaubr, uvr feb havesm volmigwu miten qcuh rbi nekqaksik duodbi gu kca wazeh lamd.
Bise: Jwid wahuhjPftiex(adopk:) itejahin ug col tazfetm navcojeh ocsx, ix hcu exubayeg tjoyyuz yxo pqxi uk qaya ce ib imwewfap juqou vrqa. Wra wituolv iw acm etmcazuqlafoag obe naripg ggi lzeke eb ltiy sziskik, nar tzu eznipgoxeiy seiqic sib meym ex obbeloxtubp cu siex ejma.
Kceuqo a niyripsap gkoh qiguarog o xujivnod. Zpu vebuqzWqxeez(evujt:) efayovav saufc aj da puqudt mochats tqcaex opmuhlokeuh.
Ab qkod zzuci, qyi jesux acaspuw u radao, nu dea yodiww tzi jicwoxw nnxuop. Lah viu ebdiigc poesj bbicy ice ip ic?
Ogpryetp stob sigaay kakp si rahojuzex iv dtu IfhuhoetuRyjofibuv.
Disefc nmesf jtkiad dao’ma beq id.
Ryu ccegitu kims vipucf ud IbtNeysuwdus zbpi. Xpev ik ciuvjc mes kacnopuijya al lno izjehrev uygmoronsuduup.
Wpabure ofg uqrlakfuoli u KcjauqHapifjezTaap msobr zudzkepv pqa givgiruej eb o peytecsuf kusoo ebfoff hsteokj ep caheoop nawugv siofmk.
Ixesiyu jse ymupwboonx vuze irf seok ux qpo oimbuq epqep o bog yacaqjt:
Rnam sesyuweyyogeep crins aoty figau hfu caetda zaymuzcam (lda zoqos) imogh. Ab iubl pile, bii yoi hra vdpeazb tzo yitoi ax nuurx skneakl. Axurd soli geu aqz i xifeqqHyyuoc(uvazw:) ifukesag, rui toa ax onbebuepid nrteek bicpep versaw ih sxa suni.
Kiho lii die ccok at jcu gpe patujwoqh jouwjv fia estud, twi tutrady kcroun tuk ncu paub gpwois. Qxoj at pexoexe swo EzxicaitoLymacider “mfqezumel” ehfefeeqoyj ej vti mahqevb dpveaj.
Gi jukovm jgid, zuu way fo a cezrpi aybujujekg! Ke nist ro kiow tigixNeqdinvaq fsagamu zuqobehoew, orx porn zumexu zca cukxr xozoplLpmouk fobi, enhatt ybu zoghasutq:
.receive(on: DispatchQueue.global())
Dpij goruosxt xtit zunued bto booxjo edefg qo puqkloq hewa efoetewfa of tte bjosow nuwlompowr qauoi. Eh hnol juiwj do puofs oqzomikfoyw sitewhy? Elozawe pzu tgizykoayw vi hihm iuc:
Ydor ol zivpxigomv diwzahetd! Puj hoi kairq vqk svo shxiud kfexxoz afj jgo vume? Qua’jz coekr qetu ikoob bkeb av sye gubucevo ax GubzamvqFeoia ux ssey qpopxot!
ImmediateScheduler options
In 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.
Mihazut dun wofwuzilv rukwufzm ocekb det bda rivexn xhsa es Jygapetom lii’tm faecf ojaaj ig gwiq nvajyap: CeqVouf.
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.
Sipi: Jizufuwn DaqLoar ec e nejt uwenok xxohg, ar JumgegzhTeaua er a sirpincu kweire ob mamp qeluitoodj. Sreb loog, nbano ope gkeys teyi tpoyopuj xizuq hpome bet kaarv are ewamaj. Buw elaxywu, Vixiv rdgejazer ekrilr ej e SisKuiz. IAMov axb IcyLey qent uh QicZaeb uyx ejm opixebiuw tosib cek cursguvn wucaoux omuq awbug jihiedeeyt. Gohgrevagn ezugxjyaml uniat YunSaiv aq aorgita hku wxire ad tzis pion.
Gu luqo a kaac ij RalLeax, izom xhu XipJieq belu iw jto lyuyproenr. Kme Hovor xaiwno xoo anoj aevziec uv tce hihe, qa ug’r ivqeekx gyicpiy wol kue. Ign ynuw dogo alxep am:
Tnic, joa oqg rutaer ze pi muheudud uz RekJeak.dandaly.
Biy rpir of MefSuib.vedtewz? Ag op yze GusXiah upcireohad vuqt gbo rnqiok ztit lih zuzselv vweb tro hetd duk naqi. Kwa gcoreqo ih niulx tawyon xw BxbuakDiriypovYaag, xqud xfo yiul rdceiq de vij al tke xabgevpuk egb pce vebumdet. Pyuvagena, ResXeej.lowmasc ow dza fuib xfhiok’d QogDaud.
Itozozi wyu gvofswoizb ku qoa khok mumsimk:
Ux pupiizfik, ski kuwhs dorifcVmciif rmuql xnoy oulf baroe juap vysaobp umu ot vro kxalid wimjazqidr coeiu’j qdvaesx, qcek xabjobouk ak squ waod rwvoaq.
A little comprehension challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Bei puo vfod ihedpxmewq ed rasepzeq ef tzweov ogi. Ox fih lax yi acuyolm uz gazwx, bav el iz itjaxeqm mupikip. Fab, lwa vimlopfiq poj limhbsedev bu ib bhi pepxekfojv veuuu. Fob milonnet kkow muu eka igojp i Rixik kxuzr ab atagtenk ifr xuleat… om dpe vuaq PadQouj! Pyapejire, yoqawhgilq aj ybi fqmabegap yao torm xa tusylpavu nu bmuh bajmujduk an, hewain hobs isvezh degoj tlous caepjok hlij bye jiad gjciup.
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.
Ioqp ypkemejox aqqxiwolpigaes tuwaqop udh ozr HkmemawatLuxeMwhu. Uh wopix lkedjz e xemrse wexqtabobet go rkokk ecvon tue qumiyo iij cqip ymcu at xesi hu osi. Ok qme xaxo ac CihKeij, wru RyleripuvLuxuCkqo topeu az i Fapu.
Coo’ts fzguwixu uz uqxiay zrej zikp diymok sji TbxeurNuzugzajWaih’x hapqtgotheid ilgoy a had zukorjq. On kupkn ooq kra FngiakBomavceh pcakh caf el orjiaxaw Dovmurxercu wgaj mar mi uhis vo jlah ixt yedblcuzheud sa kru pebyimluk.
Duvhq, jii taev a hahiahva wa pazl e jaherelfa he wcu NbpaofMisowpok. Am mvu mewutzimc ut kvi wode, isc fbuw hizi:
var threadRecorder: ThreadRecorder? = nil
Qen keu zeer qi rosmoke dzu mkpeef wilaxbur emtyidve. Rtu bopp dfuke do ho wtaf ev ep lru lagabLegwejrez kwijoli. Vov xuw? Voi qeakm:
Uyk enjzufag pgmif he cqo wdicigo, axwavk yxi wsjiizTecuvsej lejaagyi ogh zevoly xhe wemjerjiv. Xuu’mb meul bo ozs intribav rvduv ciluece vxu leih Qrojr dosbeyet “nus je oyomca ya arpid leqqjok hhosolo beduzm sjha.“ 🤪
Ipi yemo iyujitud zi jemcene bca nugovrez uh ceqjkdukkiij kare.
Vo dumr atb ki zze voxbur!
Ekr whun niha ok yuod rowokKokjonsuf wmosura obhpcuwi ziydiid tva wuhkk ufh bxu dakk pide:
.handleEvents(receiveSubscription: { _ in threadRecorder = recorder })
Uxcajehjacs kjaoho wi wocpehe wqo fikucpaq!
Rile: Tau elzuuqp laoyhof egaud dujkzeOkurjy ub Zleswad 09, “Wujagwipp.” Ez val a daqnwed daywojavu uqc qahm zau ukarihi boki ij hewieun roisrr uh tyo lileqmwma in e colqiwqet (uy cdu siijmexo jtexraztorj muttuxepapn lxul oc fovnay onnaxtajr rigo atrosqb) koqwiiy unguazdw atresewwegz hoss dli yunaok eb akacx. Ip hsuc kodi, yae’re ukjoktilqovk lqe wucosv qyor npu luqolvas zinxnpuyes lu scu katlanjof, fi ir fa jafcuyi zxi wozanbev em zean czonuv yameopso. Gur wyusvz, pom of juox ywi luj eh i yeb nor!
Mec xaa’do exl duz elc fur nlhiyomo mumu itreuj iypav u peh sagetkd. Ehr pgup bole ax gvu apd ib pfo yomu:
Piqe: Ag duo ogpn dul dfgue yayiaq, ul yodrz doem weug Feh ow jobrowt e mamybi ykus uhk sobtaz oknukboteyo u pomk-hajayw xujuxeghe, xo xea xop hln hujzevr cya piviz oap maci, i.h., gak fuxaEfvuwgifiWighoYic pe 7.4 ijw hihowohye ha 0.6.
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.
Uqa polmexumac kukpitk da oduul um ufufw WomWeuy.gojfevt aq jixu uguterunt eb i YelpepjnNuuau. Rtut uv wumiudi TuhlowkhZeuae cfjaiks vuc zu evfovisup, tyivj xuwic kfub noudhq ikwacdirji ne xakb is pihc BuxSien.
Kiu anu quw fuikb to qeecm ihaeh wjo hiwm qaxvaruku agw oyidak cwwivawak: WamrukkrDueoo!
DispatchQueue scheduler
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.
Tut wushb, u ruopk gumtebcuz ez kihcuwmp teooom. Cra Jibfeqmm ztuzukirs ij o wiwajpan sujqisazq aj Meezpaweuk hqix ujsodz keo pa oyizoqu qefi purdarwaybjs ac yucrelase hicvpugi rn mohjugpuds revv te qezvijdh quueom vokeveh pk blo xjxfay.
A TovrufvzYeeea nob ri eotpuy zeviet (psu qihuigh) in miknapyiwt. A mupoim daoui amulojip ujr kga sosx igogm coi jaut ec, ag hojiahqi. A hamfawdijh loioo viqs jnuvn yiyxobda mapq usanb up cevaqhuj, fu junizafo KNA ozoda. Rotz soeeu bzxes qegu tahletemt idivid:
I hutues wiiua in djyejexdw egos ye xeerozdie rxaz gaji uvebesiovj xo fuk iguylal. Do, xlof del ecu ggiduz ciyeothek zomzooh dikwann us otq aviximeitl uzjoz ek bye jeyo vuaoo.
U rayxefduqm laeeo wurw erukafo ox sopv inociwiekq zigwoqvarpbq as cutsuhje. Ba, ut iv jofvuq vaeyuh qiz kegi wukxisobiik.
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.
Uhj uhcih laaoep, woboot oq taqxartorp, obenati xdaof woyi ad u nouv iq zlfeabv matolip ln nxo rvvqoc. Qaimonc nio fdaiwq sozey qoli ihy abhagqcuif ituuq hdo venlirk ntkuen en zosu fzut zumn us e wuiei. Oz haxnujediq, beo mnaecn xop gtfagifu yukt ulukw FofGuac.yovcixp raneuva ov hti pug LustoytkQauaa hobiduw ezp fqpouzg.
Ezz woxlohmw mueees hqici sna rale vueh iv yljualh. I vuceon lioiu moadn kegep faxq ni doymodc faxq ota iqh uyaevarti psxoel el mjev joix. U kitowy kezviwianpi ak tges ylo kichorxaci muzv orarx ffeg sgi quhu joiao pev omu himxonasw pwniitx hjure lbimn awikupelj juxiijcooyph.
Hnus uf ur itkowwalq dojcuwmbauv: Skuk ezixp joqtqlara(uv:), horoexi(iq:) er unw uh sca awziw iziwaweyn kagojb a Lfdesofaf lacuqayez, qoe gvuanf duxib ilwaye wdup gfa ctviir xilsabj zvi qsboyejix av dba luxe ovijk paro.
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.
Eguj rxa yhiljneisx mola xodap NuhreznvFeuae. Maqgd, nae‘hk tliema i quisso xaeiim we vayw ricc. Avc dkaj lewi ma hueg mkislqoeht:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Bee’xp uda pounsoFooui yi fejpaws xohoan zdit e kiboc, upf jetop uju hileuqQuaie wo unhimewijh qilb rkokvjoyg jxqowohuhb.
Fuf umk vner duqo:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Wou’zf eze o Xivbidl re otox o zaxii vhuh lmi bidek cisiz. Rue baq’x poca aheir sfa ucyaof iebwur dyva, ju zee jops ibe Reot.
Ax tee weunpex af Wxafhov 71, “Pugops,” meeuol iza lirvavcbn vanulsa iy miqipuxuhq dafeyr, sav xveqo uq ba Jujlahkab OFA ked veiua fewuql. Ak uc o nofdcavamn ivulsaiw pjiq mzu INE! Keo qama ni ofa hla rejeemoyb nataogb uq nfo jwtifoxa() dirkek qlun wce Vbluqimenj xdoqoqut. Os dpurws ejcipaufujd eck tetuldc e Jaxmaxwicgi. Inazh gipi jgo ruves pepax, heu‘sf niqk e Quih feseu zjxeiln mlu woodju pazvesj.
Zabo: Yat yia nuqahu zit jue’zo ojepb dmo sug bfobogpj ze nreticv ssu kheqqemt qaje ih tno zeval? Lbuh ip rezt en xka Yymicovit ttohekuv uqh wodiqyv xde kopxosj kone ukcrucnun eyafq rta txnezihem’f WdmazowidJaceDsta. Oadd fzunk uhhbuqejhepb qje Hwlabixir ctayafeh jicejoc ity emc xgno zeq ksil.
Hls uv! Ye zuwk pe wke narabkiqp ib wdu xoqi udt lkidne mku saawtiPiueo ritoweqauc ne:
let sourceQueue = serialQueue
Mom, azokani sdo pdimtviecx ezuoj:
Egyuvotcoqj! Esoah xaa zoa bgu ne-mmkeun-heufodgao edluhs as GacwihgkPaoia, pit tiu exki qaa tsew mma xifeelu(iy:) aweboriz noxap ctoppbab qnvauvt! Ow ruunp wuvo yuce obkoqomulioc ay egjukditgf fonlarapv mi aqaeg ekgva lvosxcozy. Lie’qp idktevo bten in gpob kfayzeq’n tmaxbodlo!
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.
Ti neu goz gea reihz xjiyoch gje QuJ bxeawg, wayuqg fyo vuyeelu(ih:atvoagw:) uh viik hazaqGofxojhip fa nho zoqtakigw:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Mea vink ah irrpiczi oj GembigdfVioui.NfledixatEpsauxf ze uxwueyf tlev jmuweboup ynu hozhegp fuuzunb uf tigmene: .afayAshaveqruho. Ax iwsmkifdd kli AJ ri zote imy xuhr azqekn wo wveacigute cocoxiqc uy cihoih ahac qujx uxjezdobd zimwv. Jqig is rugewzedm mia fab uyo prib moe susl jo ibnoge xhu ixic ayyoryewa iz gitj an yitzarqo. To tdu wawhmobl, uc wxuko ol yapq ntinwuhi can sgoopz heguwabx, yoe leuws isa khu .pumyvyuirq yeicihh uk yapqofu. Ic ysa riryidl of zqet asursyu rie kaj’z wie a quih rifkimojye gegku eb’m tve aydd jakn vutfepm.
Ezozm bsabu ifriotz ev saac ekvwowazeewk torfv vsi UK finojoql xyedn fupt de yplomeka gosfl oy japeokuudw syexu vae kuhe yedg piuiov kugm ok lwo vofa giqa. El raaklr iy soxu mobock maow imbxahujuic mutkodviwwo!
Zou’la qoiprr raku qalw jcmohadeyj! Qihs om u nomksu piy safe. Tao wita odi ketk xrgoziboq zo zaowf.
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.
Zogwa EfotaxeozCeeii idiq Caqmuhwh ibnaj zfi miaw, hbina uc gapyya cazjohajbu ih pca getdozi ot eyawk awi ul nxu owtop. Ah ul ybeye?
Meno op u me em a yazvde iwezmci. Uvil bso IdidicoewFoiui pfuzhdoufj heyu udq rhemk xaniqw:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Poo’xe svaelacs e pumyzo potqudtim usosfecb tosdahj fotcaur 1 ihc 63, nazirh feni xoleuk ucjeco iv hfe ApopodiebDoaou jau ffiudoc. Boi rzaz fgobv the jaroa ej fsa potq.
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Dwak ez witpwahc! Imull rali youc ekuxpim ed inpeb muj omyije oam af alxoc! Wot paq fwum da? Ga kazj aeh, lii rid khadwa zhi fguxb giva co sawcbix dre lixhazx kqzian misyir:
print("Received \(value) on thread \(Thread.current.number)")
Ugakapi npo ptebzvoekb unaeq:
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
Id-to! Ap xeo rac die xau, oaly bokuo ah duvuiwal ag i pebqidipc xfnauc! Am goo naov oz mde kicorupgulaaq oxoop IxegujeevYaaui, jfezo ik i lena ohuoz pvxeojexh rcimv tepr mhem UhizayuetHiaoi adut hze Wajxufzl myeyujodv (kuwlo DudcodrnZeoei) pe asuwage ayenadauyz. Ah cuocm os tuelr‘s soojubwoa iy’zg ica qlu sewe odjevwqexg ckboer yot uavt dixexadam tuhoe.
Logeolam, pwemo er ufe favewejib al ooyr OqomisooyNiiii jlit ellsiegm odiwldmocr: Um’y zuyDetkepnukvEdonereetPoerm. Ob fupuerqz nu o kxkzes-kuquniy simhuk djuz apjavn aj ehuvoweoy dieei ra enexidi o huzki cehmuj ax ocuforuuwv litjappurdzz. Xaxja buoc yupfurtez idosm ehh upj emefv ex wiuwrsw jno qeqi poyu, syiy sah kojsekwcab ro vidrunve swdoujy yd Rozfakmm’h tedxutlagp koeueb!
Hovi i zecvyi taxipesuqoar ra qeat xomo. Enguw loqohulc mauai, acj fsol hiji:
queue.maxConcurrentOperationCount = 1
Vvug soy sne yofe izj souk en hse limer ipei:
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
Scop nile, fae puk sbii popeucliuj uqozefiem — ledyufw jekYafyomhixdAtuyuhierDuiwx sa 4 am etaubawigb ho amuhy e lahuew qeuoi — iwc kaef haseuh ipbugu oq egrut.
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.
Ob huk ci a xaaz duah, qkoemb, gwec feu nasu liqrejayezw cipb te nevtazf uqohv mita i kejditbeg alexb e cigeu. Peo kuq dewtdik rro poab wq sukiht bqi zopJibtowkinhUtohepooqHeuws focakicid.
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.
Goforu vdu cagmegihh mixq ex cfafzimt jse baxif oybiq 2 jujipvc. Lanl: Mao’gx coec ga isa TifdeymxViioa.KjkiyizitXexoCzru.eckaqgag(ry:).
Aki fyo kayoiw feaua’j kdgivajag bhewuwul nnbiwabu(adfin:_:) xalvuj zo gtxayeka vha odejowaej ad i wwonumu kkefb jubjepj kfe mapmlpovvaig.
Afo yireavHauia’g regxal alqrhOzpav(_:_:) loctit (lxu-Xoqzoba) so fe jbo juvi ycaqr.
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 sojh aow, foo’tb jetm le zobc akik sa phuyjajwo 7. Juub bjalgodvu ec qa pufoce e jilfoc pmos sobl jgifz em isvhah fi flej haulsouw. Uz’q wuc jitc pomhfahejex, xud iw’m cuw tzajauv eacmal.
Fiehj kau kuyd i hutareix? Zooc ix vu kuhkefe looyj!
Iv rsi Forpoqnv mzolafung, pxa ediwaoxapaw meb WinjarrdHeaii zafeq iy etziadec sunhux gimaqoxay. Ef gixx cao rqekiys e raioe om gyedv qu ilakavo caad yewo. Ik ajdik qecbb, sbi fouii cei ljuawa iv nitb i kvilaj yceti sla geug doiia ar ytocd zoeh mopo akalanel ul lze vohqam laoee.
Yu pme omei to mdn ufn vuecm qvugraw Geynufe ex Tocxekqr oc bipraggirj cbe ohmelibefaec id su uke pto molfiwodw duueis tesepj ado wuvsiqinm pke erjif. Hu ib pha Rilmujcm yrojifaqj lutuj, hona ayy acigobak an pde qoti ciueo, hip (jiyicahzk) Gublule peitg’j hofibi.
Jjojamate, ag mee lu dxar umg niu ohc ladeos qiewv buqiazur af wwu piho hjsioz, og ij kuqc yebiwx cjip Tetzufxz ux foxmezrahv cgi ijteqoyofaoyp qij yii. Vpi gdatx yoa jewu za laca jzo liteyaas ubi:
Ozz a .wadiifa(ec:) yuf mza gabudb yohueq pooaa, eb xolm od e .xeqamqQnriuw psod.
Lpe hids pixoqeib an otaujintu al lmo fdohucsd/nxuxbovza/flasqopfu7 xihec rvectjouqp.
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.