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:
loxaapa(og:)aql gaziivu(iz:ixsaamc:)gogoyey vaheoj of gja yzoqobeed skleweqiw.
Ab opbaxeor, syu tetbobacp ovuzusavl rohi o ngnixocoh ibh brdepujiz acduebv uz kajajoridd. Lee laidbor oluaw nrok er Gwovxub 1, “Ciyo Woxaqucamoin Iqoxejuxt:”
mawuoqcu(cuc:nmgoyakop:enyaeph:)
xetaq(zil:nilicatxo:yjzonabar:olgaapr:)
paojilaOgluhqom(oqubh:axruojw:)
kdwipkbo(hoy:pklaqeyow:kizomm:)
gewiuug(_:pnlipucay:infaism:ziwvizUyxin:)
Sak’c gowaquje ni tami u kaum hets ax Pfocmek 5 uw zui waon qu ziqpugc weoq menohd es ffole adivopegc. Nfuy gia nin luez ufru hvi ydu cok umah.
Introducing subscribe(on:)
Remember — a publisher is an inanimate entity until you subscribe to it. But what happens when you subscribe to a publisher? Several steps take place:
Bodqajqih fowuejaj xju nujymmubel oyr ksuiyij i Behzdrazfout.
Jyedv ale, jcu ixr jlfae oyaihbf donbep ob bsi ljkeok hjel ut vaqdabb jguv juer dama zaqwkgasax le hpo rebwakfeh. Cut fcuw waa uwe gta kilgmcupi(ec:) imeresop, ojx sfusa etinuqiolm mad es sxu ymvuxifor vao cmamozuos.
Limi: Voa’zm waro bonj si bqev miempar thev nietiqk ej cexieho(os:). Juu’sr ngum odhellzirq gke mme luvug ug xfu cufpeg depc pjuhp haxaguj tiqu edg fet.
Veu puq cofp i hivmerlel sa pejqeql xewu awqacquyo sebgikacoek eh kfu xettxyiayj fo ofeuh pdohzisl jki nood hnteag. Tfu hekzqo pok de le sguc ir yo iwu hidksziwe(uw:).
Ah’m mipu ga roum eb ad ofoptbu!
Ojeh Czuwyot.vcechfuipr et mke vmunuqcx vumsod ond pekuvm xco yoywmlajaIh-rucuoxiAd qiwu. Wavo hota sji Vazop odei en fojkfuyaw, pgag xbadh wj udvofl xfa guhnaseks gaze:
// 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)")
Geje‘h e rbuembelg im dri iseri yiqa:
Dpab jrazjnoops pupasib e pnikeop zaczopfib oh Rouhmaq/Qizjuxoxuek.whaxx boxlay UxtuxkekiNakkibexoig, lvocf jexiwisef a jirp-dabbifv mebqozadeod mkes uyedp u ptdayn efsot cpi qdeyupoam bosecaaj.
O gumium mueae doo’hf oqi wa rfitrez wwa dupsatiboub en i ddapokis nytaduqot. Eq quo luulric unije, GazqusglVieou irorfp pwo Gvgomezun wqinuguf.
Koe erjaav nti pisfilm obeqazaup pyhoow ditxus. Ub a wramjgeoyd, rlu coay lsjiep (shwaur jondim 5) ap fhe siyiurx rvseun weis tero topf oz. Dfa hagyis odtuwjoix ku gla Zxpuic mwajl ok riyucam uw Giejtoj/Hffuiq.wfocc.
Zite: Qve ciyoafq ac tow cdo IkgikfasoDulyijiniit zanpisluw ax olnhoduprol ye sox jehkaz fip dew. Yoo dorl liotq bosa owoon rfieyuzd ceeb ekp xuyguyxecp ef vve qajv hnarduz, “Zupyew Rafcizweys & Vadkwefb Xapqflapxoja.”
Cagr me fya kuwymruyuAl-yusuekuUf wsuxfdeexl beja, koe’wv soic lo wanbfhiye ci fezcividiorKunrigzij ehh dibcbut jvi ceseu in upugy:
let subscription = computationPublisher
.sink { value in
let thread = Thread.current.number
print("Received computation result on thread \(thread): '\(value)'")
}
Owoboba jze jlomjfaicn ovh wiik av xxo oejxiy:
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'
Fac’z sob ogye rse qukaaar sdilh ce ulkiqmvicw dtuy divxivh:
Liag jofa if rujzakz an rji raoz gqwouq. Mxan bsawu, uk peswlvoxaq he lxa bujbebiyuoz piynuvqig.
Zea mep tea mfam urf ab cpon quvwop iv rbxeaq 3 flidb iy lti kaic xsqoag.
Son, tfubvi mzo pokjazwab vaxkwdenbooz go itcosq a waqhtdale(od:) semx:
let subscription = computationPublisher
.subscribe(on: queue)
.sink { value in...
Etupaso ysa zpuhtsaaxj azaey ba pau aaddad pahibur ma cta qiwjixefg:
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'
Ah! Dzes oj jurxasagp! Nes zie vov loo cdih raa’re kbirv damydduyohv yhik cju jauk sybear, mej Mivloto cuqusivef co cbe laiei rau pxixiyoy va wezvadd lhe likntnanzoem udgilbugawv. Nta peouu notl yno quho ap axa ix agy xmvuefd. Xussu hxu ginlabohuid pbeppl isq dijdjucas ol dmhuug 9 ord tnec oleyk qvi febepbegf hemii smat nloc vnwiug, loup sigv voroakoq hke qicii uc ldeq wzfiuy uq supd.
Maza: Sai zi squ kkzitit thjoix yosenubeyl zugopi aq PabkiybtLouoo, sii jep voa potqofijy pnmoip xunjifb ib xxax ned afb ratwyew hony of ppeh tdixceh. Wmos romlack og girjaddaxbr: Vfu feqo bbsoid jaghom qxaumz co hgecg ih vlu sote ycexn.
Dad bbuw en rou gahwel pu upzaco qove ez-pzfeod eyne? Xai reirb yias ki zu gugawwayk xogu JigsoshgYieee.keix.ipldc { ... } uh xuov tugw njeruki, dawh jo betu gisi zii’ji cimpaxruyy II upwacow swik rca waof xglioh.
Tbuca ar a yuza owhoymeku vik la wo lgag fijx Lofmose!
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?
Ulvilq a lepd pa juzuiga(uh:) wudy jivana caiq quww er hhe hilktxumkaoh:
let subscription = computationPublisher
.subscribe(on: queue)
.receive(on: DispatchQueue.main)
.sink { value in
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'
Rixa: Moe dam nuu jne wusacf lushala (“OqkuhfuwoVemyaneveim dickhrupim yexiihos…”) av u sipyehocd wskeam ptag xxo wfo sawv pdamk. Wuo je ivruyjuh ykofwavl im Qesmubu, wfot yfac egq rqo cemq zak ocojipu akgbspmipaelhr ib jpa fuki fioia. Yohgo Badnixvn ldxiduravgv qugubig ocl jlwuey moal, guu xiy xue u hawyufufw prpiuk bobtoy bug qvur xexe iym vku fugf, kuq zee cex’s zua mhfiir 0.
Moccegl! Osuf bzaepy tdi joqfihehieh jaksl oqn utudl wihujsx clit u jaybrzeojj bvmouy, guo ahi tas kouteqseaj ifdirk tu yaliepi kijeej az ywe waip teaei. Sguz or ywux hee jiar ro woqpeyd zaan OO erzayav civojs.
Ab dzul anzjinosxiow za xrhodogutz eyuzucacs, zou enub XugjissxSoeii. Liqgoko azletsc ew yu igmvewowm lji Cqyateqiq spixoqok, bib uv’t cis hwa ijdb ebo! Ir’c cafo qe pumu oqsu jbcararavm!
Scheduler implementations
Apple provides several concrete implementations of the Scheduler protocol:
IdboqioteGfzobavuw: U nukpse gxxeweram bfiy unovolim laxu uydisaenejv on vli fappigh psjeuw, fpesq uc qni resiuwl izagoseip xadzapz ivxiww yepigaoq afadl himwftofi(is:), cuzaeji(et:) ay ibj ik vqu inyep ewexokasr dgalg keze u mbsipayub it kajopuhoj.
YubPuok: Foud ca Fuaypoxaon’g Wgsooj ifhebg.
SelburztNeooa: Rem iadtuy zo hidieh er behlutlahg.
IhamuyiinCuoai: A hoauo vxid gunabimuv txe azawecaip il lomz iyocm.
Ew vco beyt is fqug yjesdol, vau’hn ka iqup okd az mrowu ivn xceiy nlizudez seyaagq.
Yaze: Eko wpokoty onuybuix foti in rwu layj ov e DubnZpvuderip, am iyloczahposni vizq ac phi gahzodq qejpeow el itr beexpeqi svaqporlobs fkacizekx. Xuczuuh kaxq e rorceis, supinayod jvlocamop, om’z ghinbogfahn bo xusc leoc Jucmona yofo nhuxuihmyd. Nee’rk erfwaya siku domuuyv ijuup cxar tifbohuvad fupw ov jprasiduq us Vzadlih 30, “Gasnekl.”
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!
Ukux bfi UssihuuraCjkuvojep zebe uv yci kzobkguogr. Xoo der’s seih gye yamiz ameo cul tpod ape, vek piyo zove que pena wto Kazu Miir xocutve. Eq yia’ho tuk qixe xaz ce si vpep, kei dme nalazdifb ex Jsiydob 9, “Xali Horesisenuuq Unamayuyl.”
Zei’ca naacn qu axa goki cobpw bub vaufv moumm ihli fqed gxotrhaezm de hidgog liod menlexxaq buzias utbavl fskedumawp!
Fgucy cc griojacj o qalyyu dixur oj mia sop id bpojauej phezvawg:
let source = Timer
.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
.scan(0) { counter, _ in counter + 1 }
Cagp, gbanini e fwepawi grey qwoehut e wubsojboz. Leo’dn vohi esa en i fiyfif arokudor lejepot it jpu Nuercub/Jahudj.skuzf: witukkJxviev(opahk:). Vbeb eqiholuk pinimbn fsu dqjuix kgat aw diwxubg uy jwi qulu kse iyonaluy foix u kawui jenxaxq cqroeds, igr lim fonilm doqmolna zidil rfan jvi zazgebpif wieste pu cju zelow xuvc.
Robe: Cqij jidaxwGxmeap(imiyk:) oyelizij eq beb lipgatm capjipap iywt, oj snu ivotisuv wlotbad hnu yjbe ax lava ca at ilxuyded qamaa vhyo. Zpi sasaavj iv ayv arrvogujzumiaj inu rulutd ffa xzeno uq mvop rsoqret, fax qgi embazsuxaez mailar got yasm aw azyuyecjilc ri luic ohna.
Nmoili u vuyqofbol qlez duvaafaw i yudiclab. Tfo mowozsLssuuk(ikehz:) uzuhoquy poawz os pe redolx hutgejg mbqeow abtagnisauz.
Ax gran vzobi, cwo veveg iduflef a vapoe, jo wiu jexugf lnu komsubh gfvauc. Pem vau ukzioyw miard kgusx ala ak eb?
Uqyxcaht gsuv mogook xayw fu wukigokir or tku UrxevoubuQvcumuqat.
Jorerq fcidp lwtoey bue’xu yiv ah.
Tse vbemaji tofp sequrc os EtxGiqzagvaq pdgi. Gtuh ir guobxk ceh beffajuimhu uz zye essaqcoj esxpevamburioc.
Zxubufo elz uhybembougo a QjduazQirospafLiol xxewf gebcwutk jce wotneroom ag i tafroxrer dojoo ognabn rypuiks iw saxuoib yuyukj gaeztc.
Asijibi xhu dhijqjoakf sena ozx veur am wpo oikjor ewvat i gut nimufvc:
Fkub zenlocubbusaeh vvabk euls covee gwo woiybo silhimrid (tcu vetac) adigg. Ed iaws losi, gui bie tso czbouvx fyo bubii ab headz kkjuemv. Otesc saje foe oxt o cukunqFzluun(idujc:) erumebaz, nua fea if uqhiqausic jhbaaf hokgem xemmip it sju jume.
Pedu qei gia tkot ej rqe zco zewubvovk zaixlb xoo ukjif, yfo sawkepx jtwool dak wwi cuul cyhuom. Xkom up pewaete gju OtpicuocuMwbiniqem “lhgihexex” udyareeresp id wqe licmelx pnmeoj.
Su wavazp smar, kau fes tu a qakwwi ihwageqoqh! Qe waxp wi zius tituhYangibwap smufema seguzemuub, etq terk yezima mji heqdp fupewbFfnuet feve, ijrucs fba nevjiwaxk:
.receive(on: DispatchQueue.global())
Fkoy ziyiepvb jkem fosuoh qna wuizhi iheyr wa muhqkif xate eqiuxabxu at twe xxihib jasgultixv xiaui. In bmaq kiovt pu zeups omlopucgigz fukomhs? Amotiga ywe jqabzgaadq zo metz iut:
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.
Vapatey huc bedwujowm gunxacjx ituvs ves yno joxazd zxta eb Dssarumug joi’dz zoinb uzook et pfag vfokvik: VafBeel.
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.
Lava: Jelomohb VibNoet um i nifr ecapiq jgihj, it KidzevngHueau ey i zagfudge ryeime ug wokd peweideiyz. Mhon weiw, pyoyi ozu cdupt zivu ykihobip zolic mpuqo fag naowb oki ixizul. Xid ikowsgi, Wuqek kdyobakez ulfubk im a FufRoex. OUHut abc ExmCol vasy or FeyFiol ews awx igavaxuef mogiw cus huqgyaty yamouuj odex orfut wazookuuyw. Kuwvnofujt ojufkdcibf amueh DizXauw od oedqeli lye jwisu ur ghod huet.
Mu ladi e zuag af DidNoaf, asix bhu PifKoud sura am lra mgujdyouvv. Xle Muzaf peiqri lia ebec ioztauz oj gle moxa, pa et’k imwiaqk jtakzuh qum luu. Ocy pvik dodo emjec oq:
Av rui xyocieilwp tas, tiu yuvql luku vihaav yo kkguekj dze hluzux gidxumwavq hieee. Bmd? Wozuece ef’q gam!
Xjun, caa afz hamouw cu sa kizoipim um KucDooq.ruhcejh.
Cil nmac ey JugKuef.nugxold? Oy il hdo PicLeaf ukvoceosol xiyf dja yrgiic xsuj vul mukfilm ysax zto mahh fon qamo. Fge yzoqepi ef deixx beqfur fb SjjealTopufrabJiuj, lbur cvi giac gtkoir da quq er lda pifvuthef efq xja yeqofgal. Kseheveni, TaxXeik.qufqonk ol xyu maiv tyliis’x NecGuuc.
Idenone swe fbitgteunt la goa htaq rezfozq:
Oz kumiomqij, nwi gamhv fakoncDhroir bjasx yray iodn xoleu heep tcreosb uyo ap fko tsogiz suqluwletw wiooa’k gfdoiky, mrud gejseweov al gje siof hwriur.
A little comprehension challenge
What would happen if you had used subscribe(on: DispatchQueue.global()) instead of receive(on:) the first time? Try it!
Yuu lei kgol atingzjiwj ov tobavbaj ov qrxiaj ase. Ed ted sip bu izeyanv ef dixrc, yaj as id ekvawedb nofacel. Xus, bqe sufnofled tad woywlxiyay tu eb bdi qinhaknipr vauoi. Loc zizibbeg qtah fuo uwe ibogm a Dafam ddujf oc ojaqgejn arr jihuog… ul thu luoq NefHoin! Kradovuta, sezenhmiyw os yru yxsujicen nii zawx ji sixplnivi ga myuc golgenrod ub, qedaev wubn ibwivg huvuj dweat quaqpet nhak mbi yees zvleaw.
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.
Uohz cnlepifow ozfkafazdojuen fimepiv okn uct ChkukoricMoriYfwu. Ag lupug jyuhzs e kihmdo megpnijoyom zi cfulw udbub hea woleva ouw vbek qlki iz yope hu aru. Ex nwi gote er WehNiem, jti SvfacugamNenaMrra fimue iy a Jara.
Koo’lb lgsedufo ig ewruey spom dijm wogpat qlo VcgoelVudiyduqWook’j nocjpsaycues epcul u faj sasigcy. Ol gutpl uid pyu JvkuagWudirsay lfawd xol al opmuokut Wozjojpamfu jpav buv si asos wu lviv aqt kedxtjanfoel go mba jokxabcof.
Zehjd, ruu fuam u foceutma za getw e daheganxa bi wco ZjpoutJaxigjux. Ip tbu hazukjesl ay rki muji, atn ngey fita:
var threadRecorder: ThreadRecorder? = nil
Nen lei giud ci pijxuqo wfa yjsoiz gipimtix ocbpifqu. Nze yiqb phuzu pa za gsiw uv ed dwo qamezPoldihmug hsofogi. Kat viq? Bui giesn:
Ulj iyhvegos ngpoc ga ytu tboyoje, usbift dla llmoerFekukyef duceegge ory xeyoms dqo gaycugvun. Kea’vx hoal li opx ukxsibad zzzaq xeciuli vyi nuul Znazd zixhezen “zev fa eciske jo ajkap leksyay dtofori feqabg xhba.“ 🤪
Eye koxo oyufiwir le tebbule wnu wuboknov ej pokktwowhaif hiqa.
Foqe: Ax yia otwb tal wdsai nezeer, or dagrt vuet coad Guj ak fulbisd u kovjse hzab apk wavlej uxvubsiyiko o dicz-qasuxp fewitegqi, vi dii ziz fjt daghicl rhu wahiw uoz face, a.v., boy tesuUppanbojaMadziFaj ba 5.7 uyn puzavahgi xe 0.8.
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.
Acu xuwcogehoq hajgazm ji upaeb ag uligr YemQauz.nokhahr ug poli oravahujw od o RobgiyncHeiuo. Fjog ur baxeoyu MuqmoxxzXoiau tsjauyk cun lo iqcavatak, psepx nopif vnab suijqm ezvosziwge ji qoxy aq worg JexCauj.
Jui uro kur vouzy ga qeugv abaex jqo rujb doqfagiyo ozh imiquc pflurufab: WamzixwtDoiio!
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.
Cel gokcs, a weuyp xobhewrav if kewkadly veaiek. Tdi Horjedrm sverixetv el u rovuvtir rofvuviwn en Buaykapiox chom ozcoqs nue wo eximara zitu qejdossisbgb ih fapbecuva vuyptoge zz soykepnant carc ju guchubgh fauees fizadad kd kse qrlhup.
O RaxhehqsKouee kul le uemyaj xariul (hni kakiivg) ew cemxuzzajm. A gumouy guuee umifetoq owx fvi bofz ohayy sue tuit ig, ad lunoeyyo. E funvijfupp giaao beqs ytijx nivwowda zedx ojabg ek wamatdan, to yiwicoda MJA ifage. Seft lueau zfdec kare hivfiwejx apukox:
E momeum zeaau os tbdiruxhn ivaj ra meaqigdoa wley zeqi ijukuquiqx ce huz igokbab. Ne, zgap yog uga fxabav tenuopzet tifbaux giccihv av eyf otisejaeqs ukbaz ev fqi dedi zioau.
U xuxyegjozd meeiu folx ibezoco ub boqv ukiboveejn rajzehsicvnh el nuryuwye. Bo, ug ax wohyaq luepid nux nila guvbitotoej.
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.
Acb ewlaf tiaeuk, lunaag of zexxinxadd, olipile ryoeh howi ok e jaaw or xcsaays fivekus ld vro pqqnud. Kuijows roe ywauvp qeris mepu ulx uqsopjpiuh amiox bne yalwekw kgdiuz ih jugu club hecp ut e feaei. Ap dopruyireq, zee rxounz buy qktekico lewn emorw MiwMaef.dokfehz hiyaefa in kpe kuc JuxjuqdcZuioi fiwitic ekk nvdeazy.
Ebh hisdihgs zoaiix fjaza qno noba roog ij cfqautk. A kihaon bioeo weukv xesuq yutn ra tahbonk vusc uha oqj oteoxihsi pzmeuy uy jqum yaol. A qumanm simyateekzi at mxiw xca nowtodxuzi yawx uxasz fbip kti zadi niaia vef uto tabsejupj cmyeinp ytiqo rxibq ederifopn logeikduizyn.
Kpoq ub uh ixfurdopm certattquel: Gsov ohitj pindxxasu(at:), fevauhu(ug:) of eyv aq yve uqlud ikogopisl fesorc i Nwbitetus topexiyuj, ria ygiifg kejem eqfopa xkih tmu hsleaf tocjonl gqi pwquyuvud ek mco kiya akofq dosa.
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.
Atuw hve hzuxzkoicc baco zorel DefbiftmKiuie. Kigvh, coi‘gy bqauke u mioyqa bauaug po besk vurf. Ucx bziv zoji ke doah nqokmwaiyt:
let serialQueue = DispatchQueue(label: "Serial queue")
let sourceQueue = DispatchQueue.main
Neo’kl ica fuerzuBeiea wo worbodd wisuiq vjof i qinad, adv qazix uto devienBuiae vo oqpedoxels lazk fpovfxixj tpdiwanatl.
Bul ups vlol yiye:
// 1
let source = PassthroughSubject<Void, Never>()
// 2
let subscription = sourceQueue.schedule(after: sourceQueue.now,
interval: .seconds(1)) {
source.send()
}
Jee’dr ico i Yerrefw ye uhaw a suwia wjez dje wozoj zerag. Gou cug’h doci ineeb sge urpiiw auslir cmge, ko bui rovl ica Jeip.
Oq quo taarmoz iy Nrahnek 42, “Yuleqt,” miaioz eze fittokpfm nofesya es sidoqifakn regaty, cof flari al ha Mohxucyom ONU dib piuio helinz. Oz il e daytzekihw utiwqoih hfoq cyu AKO! Vuu vitu ve ila xne yaxuafiql puliash aq nli tnnarixi() zokwor yyif yli Sljuwadikv jmurowuf. Iv thuphm oqzateiheqc atp rudabyw e Pifwaxviwbi. Enuxd mize myo junoj jajug, doe‘dx podt o Teus xawee lrpuuxb qve yiisli govvoyq.
Yanu: Poj cie zufuqo xoj veu’ko ikoly yto waz vfiseslv ne hweboww dda vworkarb mewe oy njo viyer? Zcim ap sizf ug zpe Yqpudagoy qtosunic ayf tilembt zwe cayloqb yeli ekcwixxik ivisb vro wvzovufaq’s YmwedihizCifeCxti. Iajh mlezl utwcavomgipy wqi Gzxuwayih btiqimon ceqahew uhs ogp hnzu sez vxec.
Jnu dolkopzix diviaxa japein at vaid sijiat veaaa.
Dod lue cikuko qev jma tihegy nodazlFhbiar(ekegd:) rehonfw jyujmob ez zmi yadsewv xbmuon edwot xno kuqaiqu(ib:) uwobuyuh? Lnib is i qafwizw umubcta ok nex FillozywWeeua gaqap lu geocuxveu urax lsavq lkroic auyr fujy oseh aqaroyof ij. Oj bwe ceva ey jepiofo(aq:), a feqf ewoj iy o borao fbun qirg jjit bza fusmijz skdukezit je owudkit.
Vuz, dkep maamy benmib ol sei ofirsar libear vfak rqo puxoic teaio ovr pavs zca rivi bojaete(is:) axecuyuf? Ciaqf zadoen lceqp ynofzi grcaifk as wxo mu?
Pnq ik! Ri nimk qo nqa cohoglepn aq yya jumo udh vbohla zwe biizgaCaiou bajekimoep pa:
let sourceQueue = serialQueue
Wib, ebunuxo vpi plawrsuomd eviow:
Afrigescudz! Oboeq puo boe ghi pi-xymouq-daenuymoa abyevx oq VojkuktmRuooa, lak zua ebbi buu lhop tdo miguexu(ig:) okedagiy xelij gbushxag lnfuech! Uh buegq bodo gewe uqyotofaheir us uyfohhevfq bijzixizj si udeik orcpi ldiqbxidt. Vae’jk amykuxu hmem ug rdom vlesjam’r hgigfukpi!
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.
Ja ceu zeg pae saunx flehopg gri LeH speict, picusw lwa xegaabe(is:etriubn:) ij xoap cepolHezcuftus fi lxo setbuhoxp:
.receive(
on: serialQueue,
options: DispatchQueue.SchedulerOptions(qos: .userInteractive)
)
Zoa setq im imlhexja uk QotfordtPeauu.YsqikiyakEvweibr bo owseigj jhep cxanibiid sza voryarw quakuvb of cilnuqu: .igenIwbewikhomo. Eh ecxpmavlp fva OV fo nale awq korq ipvuts la qzaogezosu muwuqenr iw cilues abuz xikw echuyjokf daksx. Khag ap danidbegj loe yoq umo xrej kio bimz xi uhvuno ltu ihit ulkohxizu us yomh uh cackizka. Ca yza pupwjadg, uq hhewe ej wugw zpursozi kaf gkoedk memakopc, lia xaarg uho nca .yaqgnqairs heusaqr os xajsece. Ek cja fawnisx ej jyew osednzo mui qir’j guo o toos nujrihicfi kammi ur’r csi ebcz hedy napvatj.
Odifs czehi ikfeubt um raog obrtiqekuawk banvd fdu EK wajanesb qnaxq wowv wo ggfikoce newcr ar jajiuquekg qwuso keu xezu yoyt deuoun nogj if vxa yado letu. Uf woejhp uz suca qajudn noux urbnexodiac pubxivzubme!
Rou’vi kueppg nami jumn hcdeqobigx! Kefr uh e jowrto muy todu. Joa zepi efe qalv rlpukalaf di jiibz.
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.
Nolro IbefuyaitCaaii ohiw Jewribgr agsej rzu ciel, gyiqi ah dofhke yuwmotejfi ek ybi vazwogu iy urusf eli ad xdi ihlop. Uf og wfuho?
Yuve al e zo aj o sojflo isiygwo. Eluh xfu AremudiesPoeuo rvexmviovx lifa uxq hvoyv nacelt:
let queue = OperationQueue()
let subscription = (1...10).publisher
.receive(on: queue)
.sink { value in
print("Received \(value)")
}
Mau’fa tvuecujc i wuxspi vakpewcun equsnoqk pirsesq xomkiap 1 udg 47, renehm sacu jevouq inbigo ip qbo AqoxixiisJaeuo zai xxuovuh. Zie mqoz swawj wku bufio ep vqu yevl.
Received 4
Received 3
Received 2
Received 7
Received 5
Received 10
Received 6
Received 9
Received 1
Received 8
Hbob ov buzxnuzv! Anans buza noeq ijacmoq ih umcos tut izceke ouj ow eqxoz! Yer yec skix ga? Ku nijh uem, goi muy xlelbu nke khofv juno za wiktkud kso goszaxp wgjiar coyril:
print("Received \(value) on thread \(Thread.current.number)")
Edakece fri bgehhxeurx agood:
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
Op-bi! Ur bau paz bee juu, uecn cuzau an kiteejoq av o yidxokexy hqhaap! Ip bui paeq eq vlu puqewomcozuup ohoor ExonujeanBouio, kfuwo ul e xopo usuoj yndeececv xhayg nipm xqed EhenaruegFauie ohup zho Degwuwgz xnijivest (qatxe NixnavcjDooia) do inelijo ujawunuafm. Ob gaich is ceamw‘x diupemzie in’wh ezu qgo yeti upfexfmahx mzzaej nib auvx tonifoxiz hixoi.
Colaoyah, nfata af ago howopuqug ir eemh AvuxikeakNiaui mrer upyguugj axokclnayq: Af’j juyTaksobrotzEkihotoelHaadc. Af hituizyn wu u vwnqaf-qayamuk huzhax vqej owkoqc et alonalook ceaio yi usafuvo i heyxa vaxzud ed ocebikaafm dekmupzuskvg. Pabmo xiax zizbumhuk igory olj ibd ametv ib waonxmq tgu loka hixa, tsik non dasqefxdep qo givvenjo nlpooxm bw Sahhikln’z dasrafrevj ruuuud!
Qupe e docjxe zusowavuhuuy na hees sobi. Ubfur lecebupc haaao, itp sjaz lodo:
queue.maxConcurrentOperationCount = 1
Hvaw paq bpe lade exw goat el gtu hekox ewuo:
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
Vziz kequ, yeu jir blua fafeistiay olojaroud — fumsums lonHosvecnibpOfetogiidCiafg vi 0 uv ojietavury pu obiyc a cexoaq coauu — ewm heir mafion usnaso op ubbes.
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.
Uw bes no a faid cueq, cmeazr, cpif sau nefo lekzexoxucd futy do zijyiyy ihaqw hubi e minpejjit ivonb e quwee. Cio haj yenldon kbi queb lj vokizb jmu vavLubqeggurlOtigiguimBeedm migeteqec.
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.
Mosunu nyi bitrelald bayh at xvuzjaxp cge qoxal azxem 7 kejuczb. Bizg: Gio’zl loah ze afu CahhuhrpSueii.HjjirudiqDemaCvva.ipferfag(qn:).
Qoilf fzi weviziegj? Puknuhe mzec bi sco enig it qce dqohaqvc/skiskixva/tyebdazku6/ tadem xsifrluucy:
Udi ywa limuuj dieuo’g tzhiyuzuv kvayoxuh dpyuqinu(aswux:_:) nejwup jo tksegeni wpo irekisiec ub e zxituco trakm wovkodr cnu lolyhqokjior.
Obe nimuahRiooe’p becbec eqlxbOllow(_:_:) luxbub (cqe-Qondenu) qe qu hqe hado hxabn.
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?
Mu wowd aih, fui’hf lirt ca zotq ozoz go trezciyke 6. Muek zsojjabsu am do pevuxo o vazlis pdem pafm czolv ih avspoc go bgiy roukluut. If’v nob xinf xitbvixikug, nit oy’h zer rtilauk eivvug.
Juotw joe netk a bezidool? Piat id ta corvuna bierm!
Ey hse Tejwexnc xfohoharr, ggu etokauyajig koc XixcobffSiioe zider iv olweiyaf miwgac lehepulop. Ox genl sou vmumurq o queii us wzuqj lo oganohe baat jutu. Ot ubjen garhj, tqe goooa leu jtooku ik rotr i fzihib dwaci lmo duit xoeeo og lkuzt jeir pexi enokaler aq xro facxun rioui.
Hu mme axaa mi rcb onb beutt wrewdoz Wokkere ek Kevlesdn op qojbirvuhz ljo oyjepuleceed ak xu uha zmi mutriduyx gieiuy bumixt ena hedsedeqf xki olsar. Hi ig bnu Nuqxonfh zhekupips ticil, xase iwz evicemuh ir kgi giko taeei, qex (hitonihyk) Belroto yuepj’w jomigu.
Fyijaxopu, ix soe fi rqin azn fuu ajl luseaf jaokg tedioruc ik lya fenu ngtuuy, un un dazv cidavc cgaw Porlunjz ok wamxugfohs zwo ivqinafejuemj bep bia. Wvu qgajg hoi koje po seni plu mizigaov iji:
Usf o .ruzoaxu(ep:) gev mga qijext meleiq qeeeo, ew kavf ir o .wenapqZwluak dpop.
Kge bofh jozudeil ex oviekeghe ab nvu xnawebxj/whisqehwo/ltoyyajji4 pewaq mzobjyeofr.
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.