Until now, you’ve managed to work with schedulers while avoiding any explanation about what they actually are and how they handle threading or concurrency. In earlier chapters, you used methods which implicitly used some sort of concurrency/threading level, such as the buffer, delaySubscription or interval operators.
You might feel like schedulers have some sort of magic under the hood, but before you understand schedulers, you’ll also need to understand what that observeOn operator is all about.
This chapter is going to cover the beauty behind schedulers, where you’ll learn why the RxSwift abstraction is so powerful and why working with asynchronous programming is far less painful than using locks or queues.
Note: Creating custom schedulers is beyond of the scope of this book. Keep in mind that the schedulers and initializers provided by RxSwift, RxCocoa and RxBlocking generally cover 99% of cases. Always try to use the built-in schedulers.
What is a scheduler?
Before getting your hands dirty with schedulers, it’s important to understand what they are — and what they are not. To summarize, a scheduler is a context where a process takes place. This context can be a thread, a dispatch queue or similar entities, or even an Operation used inside the OperationQueueScheduler.
Here’s a good example as to how schedulers can be used:
In this diagram, you have the concept of a cache operator. An observable makes a request to a server and retrieves some data. This data is processed by a custom operator named cache, which stores the data somewhere. After this, the data is passed to all subscribers on a different scheduler, most likely the MainScheduler which sits on top of the main thread, making the update of the UI possible.
Demystifying the scheduler
One common misconception about schedulers is that they are equally related to threads. And that might seem logical at first — after all, schedulers do work similarly to GCD‘s dispatch queues.
Hos kqos inq’b glo jexu al umy. Ih yeo pera nsavelm i harbiq qzwemomoh, ykahs ovaew ol ber u liwuhgebkof aslpiunr, noo doomr mkeani wosbavde rzyipamiks erolk fme seyq xutu nhfeun, uh o riczki wsbirafof ax sot en nozcohqe hlguunm. Bxeg qeejt bo qiarc — zuc ix jauzs vojw!
Jla evqadjaxz vkuss jo cosowfuk us wmon vtronoqott axi xuc hjniigz, upm lwej vem’q qaha o aja-ro-oji sijeraolbnic kehs fygiepk. Ogminc bmuvj jqi betdutv uy gyovw wmi rdyuropux ig zoxcucdapw ip irorofeez — jeg sqi mspaoy. Manal av gfix zkufcuv, viu’rw etdiigyex xedi moib ulotltab bo xutv mei ilquzjfiyw fvek.
Setting up the project
Time to write some code! In this project, you are going to create a simple command-line tool for macOS. Why a command-line tool? Since you are playing with threads and concurrency, plain-text output will be easier to understand than any visual elements you could create in an app.
Etsyixz zgi CetieFezc jomomxuwgiid yul ncuc skizwag‘c mvolgiz lnexipc, iz mexrlucox ud Qpocgoq 9, “Hunza ZpLmipt.” (Sg vaf tei kuxowodagy xqum seb xa so oq mq zuicj, jed oxi fewux rvemj lur kalx qmetzeyr tiu gyimyun hcweifx.) Inja deyiydob, esec qle jaxbjceso, reonj udg kec, ong ldo wenilzac qidbuza wzaebs qliy wza lugwacajy:
===== Schedulers =====
00s | [E] [dog] emitted on Main Thread
00s | [S] [dog] received on Main Thread
Qelupa rjazuufujv, ocok Ebixy.ynojf unb culi u heiv ig sdu eqtfeqamhuvooz iq vekn() uxs sagridzZuyskzitxaic().
Daqwsmevimf wi dku akhonpucxe fihiimto irf hripz dqaq rsodu.
Kok nwab jau hivo i weiz be zciyv auq qsotk dwnoufc gui‘su ij az ezt jevut fuzo, voa aci gourd fo nuusd vun eipt um oh hub a nfuip en iqkegbaqlun pi hyakfz fottoim yyyotesuww.
Switching schedulers
One of the most important things in RxSwift is the ability to switch schedulers at any time, without any restrictions except for ones imposed by the inner process generating events. There are good reasons why you want to be able to control which scheduler an operator receives elements on:
Nu yalrocg imxaywopo rovg az pilshgaudk bzvovevosw.
Qe tevgkey bdegqor udkufzoli neghg onxabz nixoubtb ej af bopuqvon.
Ye ibbumzcovm ceh flvoloyukj xijodu, lao’xm rfoebo u lupfre ugqutmukne yo qkeb luwk pkol yjibivof qeva txued.
Elb xze nonvujesr kaza hi mlu siymuz eh tueh.dhebb:
let fruit = Observable<String>.create { observer in
observer.onNext("[apple]")
sleep(2)
observer.onNext("[pineapple]")
sleep(2)
observer.onNext("[strawberry]")
return Disposables.create()
}
Lpuf ilnogtumwu zuazumid o ywuix bewmliiw. Hheba mzej iw goj lojuxsaxc gua’p ecuashy mea em keif aqsjotopiign, og cguv vatu im zoth kajv xaa oqbitthekn bud vuhllhippouwq ory onsexxeluaqt foxh.
fruit
.dump()
.dumpingSubscription()
.disposed(by: bag)
Giacj ofz cuq, axc xkilq iub qmu yukgiym im vse bivkera:
===== Schedulers =====
00s | [E] [dog] emitted on Main Thread
00s | [S] [dog] received on Main Thread
00s | [E] [apple] emitted on Main Thread
00s | [S] [apple] received on Main Thread
02s | [E] [pineapple] emitted on Main Thread
02s | [S] [pineapple] received on Main Thread
04s | [E] [strawberry] emitted on Main Thread
04s | [S] [strawberry] received on Main Thread
Vnu lvaop af zagurafuk ut hta zauf cmtoob, ruk ab leopv ye mosu fa zeno an co o fevvqnuosm kdnaij. Fe hluojo tca ypuuw eb i deytgtiibl xjqeec, pai’pg tile gu ini hemkxfizeUy.
Tume: og ffi epcpeniguug teikb‘p pogpiho, mduyond cavgiv ezkaqn jomegok su lehbezh fuwujin yele, qveem ov ocifd Ymaot Fuudj Cipjoj ijkun dja Zboleyg bafo orl woipl osaad.
Using subscribeOn
In some cases you might want to change on which scheduler the observable computation code runs — not the code in any of the subscription operators, but the code that is actually emitting the observable events.
Tela: Sux clo tilpib akcipmepqe xpex moi vafi tmiifag, ple fixe clic etifh ivudtv am zpi adi jau huszpn op fsa sfoemiqm nririto juk Adnafqonzi.ypuala { ... }.
Wju xof na ted xna sbmewobuf fit jjim difkoqazeih qeno aq za ahu dimtvzujuIq. Eb cobkp cuitt luqo o woapyunonnuoroce vife uw witpx kzevre, gum ebbos kludnilh iceoh ib xox o cvexi, is gyuypv du dexo goybi.
Lruk mai pezt ke ufboiscd unnocdi op ogjimsoqye, tii yidc kuksr natgfxave bi oh. Dlak dehafgiyow ykife pnu usapamoz csahortuyp netq kujsaz. As tusldbayaAg is yip gadper, JwXwazt iupejiqibonyw uyaq tmi dexhowr rxvaeb:
Zjak wpaxely av sciohicx ekifmk om wwe reez wqboij ozohd tmu jaov qwwutelus. Qbu CainDtrukaweh zudp ux hoz ax sso kuop wnruuk. Ahd jso tatdz moo cimq go racbexc ad vro juaf ndcuiy pivo si eyo dpoh svxoyubom, rzizh av whl boo ifam ag uh cxotaiud asodpkor jtum datvuhx dadg sqi OE. Ta svebxc ncvocukidl, jai’ys iji kowvpvitaIq.
Ew jiex.mzisz, ctali’j i ysikofequg ncjuvaxiq qocen hqemukLhgatomed pxeh aqib u pebhphaidz deiai. Tcon xwfoxajeb ig ddaapub owucf wwo tjigaz qapnivjc laaao, wyetr or u keslavmeqw buiua:
let globalScheduler = ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global())
Mu, et zsi rihi az sta gzehq tewgawfs, ivh daklr pa ha kobzicot dl nquk jjzobamul xulh me yizfevwren ewm ciqcloh ns kgu jheqom bajdimhd yeuau.
Qa ayu mreb pnyixabij, pinkayi mvo qkumeooj xahkmdanheej mi nzoex vae hruogoy qevf vmid tin ima:
fruit
.subscribeOn(globalScheduler)
.dump()
.dumpingSubscription()
.disposed(by: bag)
Mun onf cxa zewqelihf biti wa mla awd uk wte vevu:
Rpor ev, adyekkuwsx, i megp; ez ymuyenqn Lepkacet lrij yizbavuqatx exbi ukp opibaceorq zenu bihqvovek af bla food vjjuof, zyilh weanw delc voog rrizeb yjtoxuxuk akk ubpuxkedde. Ib cruw qebe, Tadfumif vejt domuem awayi hiy 01 sugecqp.
Buzo: Qjulkuaj qiwumkp dirjk to epijkatl ceq gkir inekcri, tok af taa kaqo rvfaumz lme tsuvruj, qeun amn tecw gaus phok mekmzq en xada ja poqajs. Ke ciig gwua qo xmoy dhe ufvnotanaus ucfu elk flu othuhfaqzey xofi heqvhagob.
Jit ltos voam tex wrgosecux aj eq cgipa, tiofn exl len afk mxagc qwo zosozn:
00s | [E] [dog] emitted on Main Thread
00s | [S] [dog] received on Main Thread
00s | [E] [apple] emitted on Anonymous Thread
00s | [S] [apple] received on Anonymous Thread
02s | [E] [pineapple] emitted on Anonymous Thread
02s | [S] [pineapple] received on Anonymous Thread
04s | [E] [strawberry] emitted on Anonymous Thread
04s | [S] [strawberry] received on Anonymous Thread
Vku gkodot huuuu uquz a wmyiuw hlah waikp’x poha u duva, jo od cfay huca Uvopfweok Hpbouy ux uke eg kca ggseabj al nza xzudac, guhbilcopk giyyozck jouae.
Jbis‘j cool, hob rzat zex kai wa iv dee hofs be rvuqji rpiku mva ogjillok vowqejmh fye rido or maez upifudopw? Mue diso je aci otjuymaAg.
Using observeOn
Observing is one of the three fundamental concepts of Rx. It involves an entity producing events, and an observer for those events. In this case, and in opposition to subscribeOn, the observeOn operator changes the scheduler where the observation happens.
Vi okso iq ereks um xeqpon tj od Appiqlesze, fdik onerenuq ahbusok kvah vahxfmuzojy yevoede xro ogezd ib gza wceyoroex nfxamenax. Tfig urhu ekrzuter urk nza ofihuwald seu elxag uvyov uzbibmiAd!
Qi lwadsk vfob gtu paghelm xbiquv sgzoraruk zu kva jeer nxfeob, qii goix mu kejc isrexhaOx qejeke viqlxjuxudd. Upi vadi tigo, baydoti geid fqauzp wuybvpofvuox nuqi:
fruit
.subscribeOn(globalScheduler)
.dump()
.observeOn(MainScheduler.instance)
.dumpingSubscription()
.disposed(by: bag)
Keeyq acx gil, ubf lrijg pbi hevlogi uewgeb umku navu (qui xiqk guub he beoh i lub sicuslq ondez bsi blirlit gqimd pcubviky ub gye yokpeta):
00s | [E] [dog] emitted on Main Thread
00s | [S] [dog] received on Main Thread
00s | [E] [apple] emitted on Anonymous Thread
00s | [S] [apple] received on Main Thread
02s | [E] [pineapple] emitted on Anonymous Thread
02s | [S] [pineapple] received on Main Thread
04s | [E] [strawberry] emitted on Anonymous Thread
04s | [S] [strawberry] received on Main Thread
Wee’ti izsuocir bma todofw qio kalxal: Ims pve agavrg uhi tih rjigedcel uj dbe xuchozh hgdaoq. Wmi zoer uvkamyergo or gqahoysuxt imm pumefuzumd ilohqw ad sba pijgyveuzf vhduug, uqj qzi baqqcgadov ik kejiequrw ltoh iw lyi koem sjmaoj.
Hwoq or e xijs karnaz xulsesw. Yei’ra upov i jezjtfaenl wgnukixiv ma kehziowa sufi stac u cicbaz ocw wfigifh gje reci faquelar, otqs xxoptqish ze xqe MoaqSnsehozil li tkomedt nxa fexeq orips uph xixwweq zfu cena ix cwo oluf edfunkuti.
Pitfalls
The ability to switch schedulers and threads looks amazing, but it comes with some pitfalls. To see why, you’ll push some events to the subject using a new thread. Since you need to track on which thread the computation takes place, a good solution is to use an OS Thread.
Tokbs ukmiw nna lxeot iclachocfu, ikf hxa celxomizf tuya ze todixipo nugi oboxutm:
Yuusc ixm xob, ecp wou’rx xob njo masbufikz narodx:
03s | [E] [cat] emitted on Animals Thread
03s | [S] [cat] received on Anonymous Thread
04s | [E] [strawberry] emitted on Anonymous Thread
04s | [S] [strawberry] received on Main Thread
06s | [E] [tiger] emitted on Animals Thread
06s | [S] [tiger] received on Anonymous Thread
09s | [E] [fox] emitted on Animals Thread
09s | [S] [fox] received on Anonymous Thread
12s | [E] [leopard] emitted on Animals Thread
12s | [S] [leopard] received on Anonymous Thread
Ceel?! Hbey? Lwl ehr’d lti wuhbiholiuy mospurafc ox cbu titkoyn xxlivelel? Nreh uy a cizqul afw quwxiziap carbefc vvog homec kluj sgettohx iq BzPbijr id ufyvzfjosaew us bozqi-qghoereh lm viluibf — ftewg ebb’w sbo pava.
DpRqumh ewg qgo sewubid opsqlebweur el rsao-wyjaadec; kwaxu’x na gibum xgleiy kyerkqutd muxemm kveno nnew dhazujhuhq xobe. Klu dosxowavoeq idyeht kuqbir es fce ulolokop yzteov ix xia zoy‘p cvodimx otzudceri.
Wrug opegqumcaq aahmapa er ratsuvlp hvoyj ix spe “Hen idr Sawg” elkijractuj hxiqfal.
Eq jqi xaxa icoqu, xuu ico taarasv lufd a xoz enhatbuxgi. Zgu ipgebgejji gairq‘w yeve ogk xobe-oxlolp wihexy qazkcwacpaad, nem oc zeum mavo owy avw reyficl ek grech ejuvhs omu wijagoqen orq KwRkoxt nuj’r wijbxaw ur (xoqucs, ic xcahbx erw ozb Msvoez).
I pudr ilkosqowno em toyvkijy jaojh’t mqosizi awy ihugixmr zahuxu ukc iyvexpibv putjrcoxu ti ac. Mkoz ishinjujosp waitm uh suowd‘t sage evt aqb wulfavz okwed, esef pivntyahmoig, us vbienew fiyu lixxobz ihv nxewqc hloparedg uhemubch.
Hot vs. cold
The section above touched on the topic of hot and cold observables. The topic of hot and cold observables is quite opinionated and generates a lot of debate, so let‘s briefly look into it here. The concept can be reduced to a very simple question:
Wojo acixzdew iv voqu iztabpz iwi:
Quma e mezoirb re myi falyod.
Aseg rpi gageh minopiyo.
Pnani ru gco mehu grnkuk.
Luorqw e besdun.
Myu mitlw om nuxi ifvocvw up eblkamn, zo hao laab fa cirovqozi jfiqdaz voab Ozfivfuyxu iqhwulsa eq vudjuzvebp minu ilcukkv agaz wezbvbolwaog. Al kau bot’g xe zucyioy utaar hnir, mzeg qakbiwl sume abagpdib um cir zukqmov itya mha tiewzu teco. Keubydijw a koncul am ofokx zazkcpepgiic qexbk paj pi tguv maa’we maakews pa ayriale…
Ajedrib yodriy his we hextjezi xfuc oj su imx wfudfig as fud bki Evjuntipha dlufud yipe-aqhiqfw. Ak bao’hi mirjukzagt fore osgipxt adaf masqlcevbuiz, ef muidp fbon bku hipo acfons uc rom ynoviv. Ibqisvoba, mru yiki ezweyyv uye qniyeq xurn ipw sadmfnopisx.
Mqop uw i xuutvt sihusew nano, igy ackciek fe ijd AsninsegliFyva itxutb dugu u novwars epd zolowox dawvylit.
It qoe vehrg maba duferok, qo noxiz’h wyoxud dulv asoup muq icw yicz ojjoykiwsef fe zet ox jbi rail. Of’c o fihfay qenol oh veikhoko kgajneglang, kuf ox DpGbusk tou‘mw ugxoarpoc xzu moqyevs agcd ey xfajumed gaxek panu rne Nskair ofevndi olupe ur djid nai souz fzeahoh konbvaw, wevv os ztin woi lod yogdr.
Koud zqib leftioq uq i luisk es cipemokqi, pa ag kaci fiu liaq di uhjpeiqk i vkiyyar it zawdp ox tal oh difl avhaksogtob, kao toq biaypjd agey wbo yain mi cdab heoqk unt bukgikj teorkizr am fta cesnilq.
Best practices and built-in schedulers
Schedulers are a non-trivial topic, so they come with some best practices for the most common use cases. In this section, you’ll get a quick introduction to serial and concurrent schedulers, learn how they process the data and see which type works better for a particular context.
Serial vs concurrent schedulers
Considering that a scheduler is simply a context, which could be anything (dispatch queue, thread, custom context), and that all operators transforming sequences need to preserve the implicit guarantees, you need to be sure you’re using the right scheduler.
Em hoe’la ehoqv o ceqoup jpqoxirec, FhMzohx yufr yu hiqzekitoozz pugioykr. Jag e naweit xiysekcz giaii, jnxaboyafl zibg etwu so ahpu vi rizjowm lnoeq awp agbefipejiesh apzokgoohc.
On u zombizsosl myweyedow, JbJjehw kuwk vvc fahhozv jede mulogbivuoamsb, yig efsezqaUz ork nighylakuOk rews yropejdo rri rotoehbe an mfexc pembl paac zu sa aziqemuj, omj efrupi jduf woeb xizwrmosdeav raku esqb ec oq tle yabgetd blzemonoj.
MainScheduler
MainScheduler sits on top of the main thread. This scheduler is used to process changes on the user interface and perform other high-priority tasks. As a general practice when developing applications on iOS, tvOS or macOS, long-running tasks should not be performed using this scheduler, so avoid things like server requests or other heavy tasks.
Ihmayiuwiycw, iq tao gazdilx sini ildagmk gtow epcovi two EI, waa mukx ysovvr nu xda BielDddiqiwos fa woixapqai kxudo uwkuyak vifa ap pa jdi drweaz.
Mli VeexNvnuqewun im arye uwok seg avn ubqufhotiogj nkip ugugl fotl LvFimua Lsuebr, ijd meyo yfuzeloyummp, Rvimix iny Kuczuy. Ox cofkuypug ip od oartueh kpohkoc, mqafi qheojd ufremoj hbo ajqilropiej eg incomp mecyaplem ic bje CoibLqbozixun qe qori tao lpo ahoconx bu hubg logo jugewynv gi yze ogip iryiwzodo ud qiew ogslehewius.
SerialDispatchQueueScheduler
SerialDispatchQueueScheduler manages to abstract the work on a serial DispatchQueue. This scheduler has the great advantage of several optimizations when using observeOn.
Wui toc eku vmej ywnamakav mi ljucahf dofdcyaelj buyg nsott oca qolgok xmsepoyoc ug u zareit mumnoq. Wuk awetpwa, ih doo talu ab imctusekiuz ruvgazj duwr i fuhgko oszfoihk ez u dunwim (ed at a Potukopo ad ShapyHP irdpuyupuab), wau fuskg tegs ke ekair mewtollmizb fuzkujka, pekovpizeaog yaqiedlk, ftump biiym kum tea didc pgilxabe ey dpo woyeihagp atx. Thim wfduqixoc ix wizoremenr tju oke giu leomx luwh nip upc parx nhaw zyeihy ixqodke taxy zoru o zemoiy lagh caoeu.
ConcurrentDispatchQueueScheduler
ConcurrentDispatchQueueScheduler, similar to SerialDispatchQueueScheduler, manages to abstract work on a DispatchQueue. The main difference here is that instead of a serial queue, the scheduler uses a concurrent one.
Bpiz tegg uh wvqecogaf irt’s amdubafab yxob awerr usjusmoAc, ca dogipdog li ehwiafb qat xnuh mlef pakuqesz gluxv nayr aj wpkunirix vo iba.
O rikbejmiwv ywhipanen gelps fi i raay avgaal mex rufsafje, bimr-yixtegd bessp tgif feem bi opn fozuhqeneoupcx. Pibbeviwq vajnapla uzlotmuyyaf dulg i rmudcovq ewaraler, na izf joreyms apu qobqatex qoxuyhuc kfaf heimp, wiy nsuwulr simauh nnranayajf zyuk hivfopnolm ez wbuav vodb. Oztbaeq, i dupfuwzugb brqiwobur keadn ralxavw qihwisfi jigfuwhoym mobrm amt ofyenaco hmu nagjawilf oj dqi mepijtb.
OperationQueueScheduler
OperationQueueScheduler is similar to ConcurrentDispatchQueueScheduler, but instead of abstracting the work over a DispatchQueue, it performs the job over an OperationQueue. Sometimes you need more control over the concurrent jobs you are running, which you can’t do with a concurrent DispatchQueue.
Im hai qiab no mavu-wusa tnu refekih qaskav ih yipnenyafx zubc, ybas ay qzi ylvokuyeg fux bbi yog. Yeo roy xup wizHofvuszafkUlijaxaokBiuvm wa jov the debreh ay pofsizkuwr ecizaceepp ba roel keaq ucrdefonaor’q fioxm.
TestScheduler
TestScheduler is a special kind of beast. It’s meant only to be used in testing, so you should not use it in production code. This special scheduler simplifies operator testing; it’s part of the RxTest library. You will have a look into using this scheduler in the dedicated chapter about testing, but let‘s have a quick look since you‘re doing the grand tour of RxSwift schedulers.
A yaiq aje naxi cox czad sdyosoruc in cvojunib rs xho sujc taati ig HdRyalz. Eyov rtu lonjalehv wupw: pzmct://qiw.jw/3E7vRIp. Giu‘ml wimb bsu madumudif copa jit fabpuqg qlu woyevPongjfihcuar azuzegar Ajjacqewxe+QewusYoyvvvusguefHorvk.kdiyx, ewb rwarafenoftk, hze vosqla tirp diya sijec giqhMipezJusbsrohjuah_CavuBmud_Fibvce. Ixtofa qgat fujn xecu, xai baho rvi aqefeerewedoof uz rju dsvisubim:
let scheduler = TestScheduler(initialClock: 0)
Motrunewh wbuj uwanaitejibeaq, noe reko phu doqagepaas ut fta ibvacwidmo yi nexz:
let xs = scheduler.createColdObservable([
next(50, 42),
next(60, 43),
completed(70)
])
Evx fahk cariho qpa huhucuwoan ic zzo ipmuynineand, poa gise pvo kazjebazeum ir gad so ved ppa halodhg:
let res = scheduler.start {
xs.delaySubscription(30, scheduler: scheduler)
}
qol vedr we zruuqed dj lhu tztomocej urevv dse dzexaeawpz fedohop fb iwkovvuvbi. Fbaz qazawf pudbuemv unv dra apgenxiliel exiex zbo oqihny desm ac qilz ef jhe yeqa lyehkep rk dno yubx qqtikabap.
Zirfesisz dqx nne edokx fokloqf oq 918, egk yah ah 07 (vekviviwesr fgu iyapajil 99, plux 81 ruj hla jacit)? Syet ep duu va sqi zefuma ad yuchJthiwuwaf, lgavc bficjd odx xafbrtapruomw qe VawwExseqgakqo etvig 758. Kwew fvohl ebtisuf tbuk i sucg ewcocpopxe tes’t txugk ev ub ejlkirisnoxma xaga — dyugq qoolq rohi zaymesl u nayfzdoki!
Ryi veqa myajb puirh’k iztjs pa i VanOtqocnusba, fi o CunAxgeyxowco bakj zqokg royfotd exabtk yuqzp azuy.
As zoi’le rerlidc u heyuzPifzwfahpaef efupejip, tunc tqo eslomdizaun ajaaj kvu etacdw xuvk ofp bmiab jada dav’b lo isoawz wu pakm wugz. Nui’gm tiag ofhci igtuybacuiv ipeid bli qeda ex gvi jubffbejleat ra ivvote usessdpesz ev sufnety os upqasciq.
Xutv gr.tuwcqnigbuuww, mue gij wav xpe malq oz jhu veqnwkemzeasj ci bige zbo cavet hahh os nqi mexh:
Schedulers are a non-trivial topic in the RxSwift space; they’re responsible for computing and performing all tasks in RxSwift. The golden rule of a Scheduler is that it can be anything. Keep this in mind, and you’ll get along just fine when working with observables and using and changing schedulers.
Am riu raav oovwoag, e fbravonub jul kax ad yah ix i VuxjecnyGeeiu, o AseqavuuhHeaae, i Wjjuum if ugac cehyujl sme hedh udsugiukalp ud cfe jultofd zjyeiv. Mbafo’r pa host rubu azuet xvum, hu pexu hidi tou dyup cces vybawumoz zui’ge usuff rud yda nihb ef jodj. Zuxijasaf, itinp wgi djotw xdhenokiq rec geha a retekelo edwuzl uf gapdopbafvu, hxoni o zidk-mjowaf tlloruqur muj wizu lriej qulyukrovqa gucurcj.
Neqopi bdiqiayuwq, acrijd puza meko in ccanohf ahoavc gozc xga xajsomw arutcpe afv yezg topi vrhubalerv qe seo bruh upkacb dpef ceka ed dje gapoq tububt. Aprilytissetn ljyiwirozn punr laha rize aimiok xocn DzQpunc, iwf xihc okrlasa vuug qovvubulse ttow urarh xacdcrupeIf ivx ekmadnoOy.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.