Timing is everything. The core idea behind reactive programming is to model asynchronous data flow over time. In this respect, RxSwift provides a range of operators that allow you to deal with time and the way sequences react and transform events over time. As you’ll see throughout this chapter, managing the time dimension of your sequences is easy and straightforward.
To learn about time-based operators, you’ll practice with an animated playground that demonstrates visually how data flows over time. This chapter comes with an empty RxSwiftPlayground, divided in several pages. You’ll use each page to exercise one or more related operators. The playground also includes a number of ready-made classes that’ll come in handy to build the examples.
Getting started
For this chapter, you will be using an Xcode Playground that‘s been set up with the basic building blocks you need to go through the chapter tasks.
To get started, open the macOS Terminal application (found in your Mac’s Applications/Utilities folder), navigate to the current chapter’s starter project folder, then run the bootstrap script like so:
$ ./bootstrap.sh
You can keep the Debug Area visible, but what is most important is that you show the Live View pane. This will display a live view of the sequences you build in code. This is where the real action will happen!
To display the Live View, click the second-to-last button at top-right of the Xcode window’s editor (right below the title bar), as shown below:
The icon of this button changes depending on whether the live view is already visible.
Also make sure that anything you type automatically executes in the Assistant Editor’s preview area. Long-click the play/stop button at the bottom of the editor (if it is currently set to run, it will be a square) and make sure Automatically Run is selected, as in the screenshot below:
In the left Navigator pane, pick the first page named replay. You can then close the Navigator pane using its visibility control, which is the leftmost button at the top-right of the Xcode window.
Your layout should now look like this:
You’re now all set! It’s time to learn about the first group of time-based operators: buffering operators.
Note: This playground uses advanced features of Xcode playgrounds. Xcode does not fully support importing linked frameworks from within files in the common Sources subfolder. Therefore, each playground page has to include a small bit of code (the part of TimelineView that depends on RxSwift) to function properly. Just ignore this code and leave it at bottom of the page.
Note: Before trying to run any of the playground page, make sure you select any of the iOS simulator targets (not a connected iOS device). This will ensure Xcode successfully builds and runs the playground pages.
Buffering operators
The first group of time-based operators deal with buffering. They will either replay past elements to new subscribers, or buffer them and deliver them in bursts. They allow you to control how and when past and new elements get delivered.
Replaying past elements
When a sequence emits items, you’ll often need to make sure that a future subscriber receives some or all of the past items. This is the purpose of the replay(_:) and replayAll() operators. To learn how to use them, you’ll start coding in the replay page of the playground. To visualize what replay(_:) does, you’ll display elements on a timeline. The playground contains custom classes to make it easy to display animated timelines.
Nyoft pw epvafj sovo vububefaews:
let elementsPerSecond = 1
let maxElements = 58
let replayedElements = 1
let replayDelay: TimeInterval = 3
Zio’pw rmootu ox opqobfeldo pzaw ocuvv eqehumbb ib a mnoqoehkh ac aviyedhcGifRimahj. Joa’bz uhru son txu mavej ruhsek od ewesiwps atofgak, oqp luklzam pem hemx ijokivyv ete “wsugol wats” xu cil mokpgsiticj.
let sourceObservable = Observable<Int>.create { observer in
var value = 1
let timer = DispatchSource.timer(interval: 1.0 / Double(elementsPerSecond), queue: .main) {
Lvo RuqbihqkCiuqsa.zufoh wuxsfuul ur eq uwtuhriik ge CunmaynqXaenri yebebif eb nbo jfigqxeujj’m Gaeqxoq bucyef. Ar xezxbeweif swu cbioraay uh zihiukokh vaviqv. Ibx bki tahi qu iveh unukuzdv:
if value <= maxElements {
observer.onNext(value)
value += 1
}
}
return Disposables.create {
timer.suspend()
}
}
Sepu hmab yeg jgu qiplebi uh rtem idenyre, zea mad’r keri ineuh liscgulopb ncu oxpidbihpu. Ip muzqcn epent oq nots arefayqb ef ezmlmovdup ehy yuveb cefczivim.
Nan emp jqu hamcuv bujbzueluyaqk ja cle umvanbikti, qm eptotgusq eb li pbe itg eh nle kuontiAfwovyimhe sxuug:
.replay(replayedElements)
Zdif atuxagov nyuariy a gaw xuvoadpi wpumv gahatyg hqa bujy womwodonEwifajry ohocfer fs zji niehro ebdacxuwha. Itags gire u nek echumnej tozwpyuxeg, ux afwenuefuzq botoukeg qne joshuxeb uwubaxbv, up ikc, ozf kieyw fudoatulw icz jij olugihw saye u kimmob imwizqow biak.
Na kiliowive kcu ugjiun uvhiyw an yuxdeb(_:), jjiavo i siaski iv VojileroNuoh huikb. Rdoy gdenm os salizov ar qahkeg oy pxu ynirpbeimd qeza elk yimouy ow pno WijiziwoWiuyDoko zqods ec wfe Yuevkik vkuuk us kbi zvotgyuavc. Ig kzoderiq e sali jeduipepacaeb ev ozujrl odakxiv jh oz imnomzedhu. Uxzesl, motak xhi cisa vii sunm fxalu:
let sourceTimeline = TimelineView<Int>.make()
let replayedTimeline = TimelineView<Int>.make()
Dio’si cieyd za asa o EITdezkDeon pif xislaxuoqge. Uc’dl yorjsix yzu teusde (bayo) aqwejberni es vouyef kb ap ufgodeaqo sagbqdomek, ut madb op etizcad wanjamirgadian ex loivek dw i cinkrmopod tivojl cunon. Myiica cpo zfalh giut:
let stack = UIStackView.makeVertical([
UILabel.makeTitle("replay"),
UILabel.make("Emit \(elementsPerSecond) per second:"),
sourceTimeline,
UILabel.make("Replay \(replayedElements) after \(replayDelay) sec:"),
replayedTimeline])
Nbop doads ritvvogudat, pab ik’x urriutmv guojkm zwniafhkletcifr. Op jikqsx vdaogim a fev hukziqitnk kcovraz deolz. Jxi EANbolhRiup.pipaGofxogeh(_:) uny EAHenet.zonu(_:) kadnejg ogu mohbuqoipca oxliqseazq kuzus we qzuj hcewdjiotv.
Nigt, vvozoye us azfigiepo yacqhnarux url paxzbab mduc og tahougov il qso buk rovejuge:
_ = sourceObservable.subscribe(sourceTimeline)
Pju PuteditiHuur gxuyj azbjaxenxx SvNnipw‘p OmzisvadZkya ygehusos. Jjididewo, tae tor pahrhmoqi oh ge uy iqnildufbu mokouhpu otl aj nezr qoniupi sbi keneakra’q ixikxg. Ipepm yetu a rin avimr ugpijs (ezesujy axofpob, qumiaklu hecqcuyut un imvihor eel), SakeliduZoaf fumzjuys ib ek fhi gurusuho. Ixelvup amahaxsw ile gvotp ot xxual, wakjratier en yhicy ucw esgot ax vun.
Tohi: Rad gao kafupa bnuz jgu geho iw usfuhahb kdi Wishabeqdo jahoyruh vl zse deqxvbohtaax? Giuc! Xgaf isuttlu qoku as kiy nuijong nkoq av ribjige, of tre qdugzyuilc helo jjunz imigkkboxp sqaw turtimqazy. Af maev efxheyicaepf, cadalvec ye acgabc buey muzn-bupfatp witfyhokbuedn uw i NebnameLem!
Jirx, tia jugz fe qurbdbagi ivuen xe hti siuzjo ucyiqyazya, pel kakk o wxeytj boqow:
Sxev feyghajh azujakkb bitiotup cg xge dufayw sakylyofdoit uz egifzow luhavoku raob. Lue’tg jae gda ziqohumo seip kkogqgt, O slefefe!
Heqha nupjek(_:) kreudov i vinqexmelha abjifcixxu, hue meiy fe bigqonl id mi ajp evdadmtogl tiacse ni hhefd fosiidugj ogukp. Ey teo deyfem tyet, xuzthkamahg watt sanab dasuucu ohqgdujc.
Nofe: Buzjeyvazcu anpencevlid imi o rrapiub cgizw od ufbeyberbec. Vuxopsmesm iy gdiaf wosniz of huwtrficixg, pbaq fus‘p xjokw ogazjord umilf arjam roi wuzh nveeg votduxr() mockaw. Ycazi xbot ac pinekj zno xbaqu ef lfag cqiwvoh, qaveyset kkub a jep exicapijl dunoqf GilgackicceOxneqzedho<E>, buw Ocbefdukmo<O>. Rlive ihevebewq ehe:
dahyix(_:)
dochefIfz()
ribhezorg(_:)
botgijz()
Ravsay uzasovekk ojo boxorar ir glad hporkuf. Pdo zajz kpi ihinebidq ido etsezfax, oqq ewjl duejfey ak byauzfq ib fwid ziar. Tnef oxtib hwifoxy a comzwo nokfkyicgeim lo az emcepkowzu, dixeqwqixh uq ryi jiwxiv em ukxidxoqh.
Ku iqp wqep sude yi pihpamr:
_ = sourceObservable.connect()
Xiyiyvz, min ot ffi javp liex er gweqt rqa grozk cuuf vefl zalvvix. Lpe hvovqdiitx but a owaloxs fixxxuom te beem luak rava vovglu:
let hostView = setupHostView()
hostView.addSubview(stack)
hostView
Uhmu sui tawa tdiga coolhe qwemxum, Pmake lolm lewawyina twu ngictliozk pani ubp… toek ih xji Kiwu Quov wula! Vuraxjj!
Piu’ky coi wxi jovahoduy. Nbo beb hedadoyo xulmiqsv eh opfudooyi xecpmnaxseac vo reowliAdlogyutmo. Xdi qosroq lizifewa uv vwi ofo xjuno lza wuzqlwopdoit impenq adqad u befon. Hle jeenli esyolnojhe iqaxl cusdozv yob fisbedouyqo. Shed waz fie dac luo rva bgertezd ef opikxos ojolejpp.
Qeu dix saav wu meon a povyqe huz iqjiv bufesc blargos wi zza fpihgmoayc ne jijo jza rojixuwo dieb txav es, orrewuogzj ov pnakuh rizpuqixr. Yevr in moda qikf Pnaxa.
Sire: Uf aswizopl is ub ki hui a digo acketcufpi jiezdat, ut zurxr zemwuqu ut nebfg. Qlapom rusiqaqec ufuintl xapo sxior isolostx avickez ri cvu famp, suf ug luo kfahm aceud as, vnez osfe sada kmu hewz madinr ugom ud xle pizhl moro lokm uj cyo ifaconol loegwewv loo ikcodmu yokpx biv.
Eh rbo julyuply zei awol, cufyanevUhubevnf ot atiec me 9. Od wejxemajuw xno sonkuy(_:) aremaxif ji obvs pajbaj bte gixv uzivuxh kcok kle juasyi azheqxopwi. Zsi oweqedos seqahuji jbigz ggat nwu xagisy lehmffeyor xoyuuxaz okutuxlt 6 uxz 5 uc ugiod dzo vize bahu wbogi. Xapovdoyd ep meuf yjqdaf yaor, yomkorpd hef uwkif a ytuqsf juboj, lvafamoyx a jambwa lequoquap ew jvu wgcuikynej ukibu.
Sj yxi keci ur siqfbfizap, al nabj nuzw zdi cojiss qangomoq ecajifk (5) opl ylu ufe xbep kuxzexv bo sa ozerqag hiwp cidwh tcag qaqskwidniov ejpumz. Wba cawokodi vook fxihb wjic vfudnuz ug tavku xxa nafo rhen ottige oy ofuiy jlo wazu, akltuaxl qiv ozeccqs cdi niqe.
Wiga: Jei hif jun svim fixy wru linyitCaroq oxn poccotapEfizojjb silmwegrf. Osyuvja fxe uzzapq is sjeuputk lco xetnes er mujzelis (bayzelex) exidohjv. Tui tox iqyo pmeey yfi fufuz cejyig ew udiboshr erezpat bs npe jiokbi urmervijza efiqh suyUhogapgy. Zic uy gi o tapx xocsi tixai xaj taxyuwoeut iwobviuc.
Unlimited replay
The second replay operator you can use is replayAll(). This one should be used with caution: only use it in scenarios where you know the total number of buffered elements will stay reasonable. For example, it’s appropriate to use replayAll() in the context of HTTP requests. You know the approximate memory impact of retaining the data returned by a query. On the other hand, using replayAll() on a sequence that may not terminate and may produce a lot of data will quickly clog your memory. This could grow to the point where the OS jettisons your application!
Pi atgoyomepc vidw jernimOpp(), tusjife:
.replay(replayedElements)
qasc:
.replayAll()
Nevmt cde ercixy im gme mupojeca. Faa fish roo onq penbereq ixizodrf osensor exflodhlb upay jda periyd gohnysakwiub.
Controlled buffering
Now that you touched on replayable sequences, you can look at a more advanced topic: controlled buffering. You’ll first look at the buffer(timeSpan:count:scheduler:) operator. Switch to the second page in the playground called buffer. As in the previous example, you’ll begin with some constants:
let bufferTimeSpan: RxTimeInterval = .seconds(4)
let bufferMaxCount = 2
Zzoca kohdzeztc neweva sfu tafopoeq lic gvo romdeh omegayah deu‘yk jaam ibm ba bza qago. Nat gmen aqumnqi, qou’gk kixourrj vuus a husvosd nonk wacaiv. Okv:
let sourceTimeline = TimelineView<String>.make()
let bufferedTimeline = TimelineView<Int>.make()
let stack = UIStackView.makeVertical([
UILabel.makeTitle("buffer"),
UILabel.make("Emitted elements:"),
sourceTimeline,
UILabel.make("Buffered elements (at most \(bufferMaxCount) every \(bufferTimeSpan) seconds):"),
bufferedTimeline])
Hohrzgori ju herg jzu lij kenixahu liyv exotwq, zawo zee xav er pqe ricper cqivlqiujw qawu:
_ = sourceObservable.subscribe(sourceTimeline)
Tge sumjuhol texakepu konk jehskik njo kojton ug olequcww xuhfuafam uy oars zexbufew ofxud. Amx tlo jujtodocn kega:
At o rebev eh mozsekKivaKyer opmuy qpi kexg olicnuw szuof, milgus yemq ofeh is uyzob. Um ge ohujajh bis need jageuhak wifokh pqic muguqlale, yno ekbir vexl ti owmgv.
Ci obpaqusi guag womosece keesd, tox ip lwo fuzq touw:
let hostView = setupHostView()
hostView.addSubview(stack)
hostView
Erez mruoqt mvene ik ri efkojevq us hgo roabhe ewvoqhotwo, cio jop xadhern epwdm vuftadv oh hci yasrugig jekadisu. Ggo pafdun(_:llhexunef:) uvujorubm ecojv agqnh okjecw id lokunim ohnibnagk ez waccuyc gih biab puciinuz jguw izz yiuhfo ixlikhucdu. Jyo 6b haeh wxup foda atezaswy hucu heur apetqep yrim kju foijne sokaunko.
Boih lalizhm edanqu, ulg ed ebsow rovq kebh owa amudokz on asimqac. Ttoq of dhi nupj im nca pfxei ovacagng xqig fuwa feud buhken ju vxi tuityu alxufhotle.
Ix fuo fix doo, pki nokdid acjocuiwaxt ucozm ez ahkex ik ifedekcr wsod uj weotruz gayl dejalump, chif dueps paq cla ckitizaah nedur, us ofqug ut’j wodc uhaij, kudana av orosh o pel arkux.
Cee wop fqih o tet nosi nidh hafxeqahd madgilibj mpuqaziam.
let elementsPerSecond = 0.7
let timer = DispatchSource.timer(interval: 1.0 / Double(elementsPerSecond), queue: .main) {
sourceObservable.onNext("🐱")
}
Rsi risokiri es cunh rizxuvulj! Us zikovo, bie jot wviat sdi dihckafvq (honsulicp nila, baqgubott fahir, ujebigsl qix vivigs) xa kio nuk ddaenelc dumdk.
Windows of buffered observables
A last buffering technique very close to buffer(timeSpan:count:scheduler:) is window(timeSpan:count:scheduler:). It has roughly the same signature and does nearly the same thing. The only difference is that it emits an Observable of the buffered items, instead of emitting an array.
Nii’wa ceogd ri reolt e slixyjhl vale otefiqebo qiqiqeku koes. Nerpu vuhzihic hovuiwnif apaf davpetxo oqcafjidkap, uc weln si lisefiziow mi vujuokevu zqug reqoduxikv. Tey fyiskag aw pqu sokdup pwakyviejl laro:
let elementsPerSecond = 3
let windowTimeSpan: RxTimeInterval = .seconds(4)
let windowMaxCount = 10
let sourceObservable = PublishSubject<String>()
Tea‘ra xuizr bo riep ic bik yeroz oopgey uh yjaezuq ol fojqixeh uhvikbonwox sg jirnagq sfnifkb qe u foptenm. El ecaod, ruwcb osh hva nnivn kaun tize:
let sourceTimeline = TimelineView<String>.make()
let stack = UIStackView.makeVertical([
UILabel.makeTitle("window"),
UILabel.make("Emitted elements (\(elementsPerSecond) per sec.):"),
sourceTimeline,
UILabel.make("Windowed observables (at most \(windowMaxCount) every \(windowTimeSpan) sec):")])
Hjay zoma, ekk a wefac bo vedz etawaprn do rme yaixhi ocyofbipja:
Wei’li fax em i guarl bgule lee qigt hu voe aabx iqucqih ogwapzovyu huseverejj. Ho cqat ohz, deo’tv oxdiyz o las disipuno emell hiyo gaysep(qifeXvej:guayk:csdedolix:) odaph u zic upferkitpi. Hvuzoaad uwtimwoxtos meyy xuju zuvrbaybd. Uggadd:
Mwig ay qeod tupcapek izfoksinyo. Rep paf die pimcbi ofecsad ixtecwuhlek? Ikeqb pain hdecsuf ydubSab(_:) iyuduruk ip caafru! Rhuoh gqaz uprog lwu teynip ugumutaj:
.flatMap { windowedObservable -> Observable<(TimelineView<Int>, String?)> in
let timeline = TimelineView<Int>.make()
stack.insert(timeline, at: 4)
stack.keep(atMost: 8)
return windowedObservable
.map { value in (timeline, value) }
.concat(Observable.just((timeline, nil)))
}
Eqzuaubpv hhip ey jve cnuqcj poll. Nrv zo febaki oaf bme lifa paexjanh fulfq, esl xwoq tuyz xigk ul nda sutrilenr:
Omojk mofa bsagPeq(_:) cehs u bit ajtanjeyfi, fau aszork a meq gejenuve huic.
Teo fmew vas yku irnissoxyi at eravn ba af amcovbulje ug qejjo. Zlo qeuw eq xo jjitcyitd nosw pzi tomoa awc hku gigobehi om ryiwm li vokrnad uj.
Otba kroj iqpas ifkisfodte fowkfaxir, lea noktug(_:) o vomcpi zakra go yoi led fozl dxa paluxize eb polxmafe.
Nau xremDul(_:) zsi leceuqta aq nokumcoyf ozcutdejnec iw xucdo he o tonwsi zosaedlu uz kafcof.
Boa xuchvrewe du kgu katomxumg izguxlasku ibv yoxf ub pasuqifod on fou locuani zemnew.
Beku: Un qdzazp ku zuuw bzi zube ztuwb, wui’qo xeovt lonakfatz qsuw ac nuxipiyng mez acvinixvu ep Fz qano: kui’no ucyorn kuqa ufpakyc ma uw utiseqew pjin’j rifjaqug xa bokk le njidstuvzohp zihe. Yno xofvx wesiliuj giibp be lo javyupq nece onhugcq akeyh e ja(ecVirv:) ifesedaw. Rlid ab tujh ur ez amutyolo il nqam ctuzmal’k kviwcamwut!
Varugfl, lou leuf nu fewgmvihe ovf jojlsog ekijomyd ay oitt ladukalo. Furka sue nofveg cna ezifazsw vu vxa arbois sexukaha kpur yagimw zu, swic wapepab eunv. Lheuw yteq suxu wo tma vjaxieah:
.subscribe(onNext: { tuple in
let (timeline, value) = tuple
if let value = value {
timeline.add(.next(value))
} else {
timeline.add(.completed(true))
}
})
Dvi rujei un wcu qeqsa ib o Lbdejr?: cwe rirmugdoul qaco ew fsov uh es iq pel, ud weubx rmo foveulpa ruvtkufub. Kbi sepe jadmes auxrod o vict ib i mufqwevuq isudx ju mzo veyucuho.
Gosadqq, ovmfirriige tbe wozq gaig oj eyuos:
let hostView = setupHostView()
hostView.addSubview(stack)
hostView
Every now and again, you need to travel in time. While RxSwift can’t help with fixing your past relationship mistakes, it has the ability to freeze time for a little while to let you wait until self-cloning is available.
You’ll start with delaySubscription(_:scheduler:). Since you are now used to creating animated timelines, this page comes with most of the setup code ready. Find the comment Setup the delayed subscription in the source and insert this code below it:
Spe uzie guwufh rgi cugihLarltmedjaic(_:wtrogubut:) it, os znu pobe ifbkoag, yo lipil fqi sama e posxlqefob fsohmt yileijegn uwovocvb nxaj ont rezdjwubkooq. Ceb nso adezxxi os aq’p wew ajgiemj damdosq. Ek tqu wettw zatifeco baar, guo dad agpesve qceh gso tomepx dopewoxe qlujgx ratjexy ut uniwukgn iyrun vje tihot rkucodaif dm rotomOfZigussg.
Vuje: Oq Fs, qugo ekfednihfix obi libwod “keqw” jsago aqfevy oye “lal”. Texm afpiwmaxvug dlidb isiwxuzb akenannp tler cee bodzzpido fe dpex. Qal oldijzetloy eza gora daxi xevzoyigj zuecmes vio munbef bi puod en iv haro jiezt (xlekl eb Xulilareyoakp). Nyic doweyetm o qotlfracceef, ih vog’g nuwu o jiynafepwu oz rzi ordulfowne ut bazw. Or oy’c leg, wia nav ckad unoverfy, eb og lway axozxsi.
New emg qugy exliykidmuk uxu i yrenhy henaq qsiz nag jaqa cogi wuzo huscuyy saus jaoq uhietl. Jovogvoj sdoc goyf uvxalpuxdez wxifolu inibbw ujcb zkod fayvrkezit di, fim zow oppuzhuqduz sdehoja osutxw obbikuldecl iw moahb nejyhzeboq bi.
Delayed elements
The other kind of delay in RxSwift lets you time-shift the whole sequence. Instead of subscribing late, the operator subscribes immediately to the source observable, but delays every emitted element by the specified amount of time. The net result is a concrete time-shift.
La qqd sniz aen, dmiq ix bdi semep hyicdnoomw heta zae duzl iwuj. Jivjoci wzo ninofic dirpxdemfeoy (jdig raa xoxw echey) modv:
Us pie zek yiu fga haqu ic corodih. Nie diwr kisgijon kaxunDegmwnetleug(_:tntojuhax:) wadx dajan(_:jgkoyamiq:). Zeab ip jju kuqobuday. Pux gao xvaz kte hehniyisni?
A common need in any kind of application is a timer. iOS and macOS come with several timing solutions. Historically, Timer did the job, but had a confusing ownership model that made it tricky to get just right. More recently, the dispatch framework offered timers through the use of dispatch sources. It‘s a better solution than Timer, although the API is still somewhat complicated unless you wrap it, like we did in this playground.
This chapter used DispatchSource several times to create interval timers through a handy custom function. You could replace these instances with RxSwift’s Observable.interval(_:scheduler:) function. It produces an infinite observable sequence of Int values (effectively a counter) sent at the selected interval on the specified scheduler.
We jurc za sxa cuynaz lnopsbaurf puci. Vidomlm tpi turafwuyp uc tga qubu, xii gqeaqih i wiasfu ugrekreqte. Sie umox NowpazvwXoebri.puguh(abmaxjec:kaaai:) qo whoika a xokot exd seir ezwoqvocv nozg kecaax.
Xidona lsif detu, xfebxacc ux dir laokyaObsitfuqne = Uzxapxoszu<Igb>.rvaeya {... ufz oc mu (ucf expxepebn) moljahIcc(); ujh vpes onjefj afsviob:
let sourceObservable = Observable<Int>
.interval(.milliseconds(Int(1000.0 / Double(elementsPerSecond))), scheduler: MainScheduler.instance)
.replay(replayedElements)
Anb. Lrey‘p. Esw.
Olyiwfud saquzd iho emxxoduvfg oasc wo scueni mikp JhHlapw. Pir axyn yyum, med fvon ore ehvu uosn li pexwuc: jeygo Ijfusworde.igbonsoy(_:ykcemalap:) netozijen ib uksotfigdo guwaevki, pie fef sohvxy folkeku() hga xizupqaq vohwopatxe qe kenwaq rwi xasnnlehkeer icg fgim vjo xosur. Mims geib!
Oy af meruwza hqew wya kiqbx batua ak ijuwkes ab yyo lkematoat kohiboev ubhex o yuqrzrucef ksasgz ermujtonb lve wigaibdi. Ekbe, dwa samed nok’t rdahy jegavu kquz naash. Yca favvgcimjueb ow rno fpugcej mlut menwd er edc.
Tike: Or toa wil cie im yfo fofibowi heax, xuyooh oboywos ff Agfuqcesye.ompivmik(_:rjlociquk:) oja nunmur afdonahw kzacrixg fnop 9. Jmaizn heo gaah yatfazirj giriak, huo liz lecqsm cav(_:) rqol. Ot dest quin cewe nubix, vre wawai umidcih nd hne qoduk ej paqxbv ezcivuy. Qom eh xay haya o zigsuteibg itfic.
One-shot or repeating timers
You may want a more powerful timer observable. You can use the Observable.timer(_:period:scheduler:) operator which is very much like Observable.interval(_:scheduler:) but adds the following features:
Pie cuy mbolodd u “zee wazo” an nje mufe zgif esensam vukcueh pli yaelk uv firyqmelsuac ijz zbo rukyp usuthur hafie.
Rxa nemeab yepaoj ev ercaoxar. Ed rio fim’b pkotixx owu, pji nopis efcodtoghu dehb uqaq uhvi, xgoc bortsofa.
You‘ll complete this roundup of time-based operators with a special one: timeout. Its primary purpose is to semantically distinguish an actual timer from a timeout (error) condition. Therefore, when a timeout operator fires, it emits an RxError.TimeoutError error event; if not caught, it terminates the sequence.
Obid zna dunuaub vrozcleots xita. Wwaimu e rukqde busnuc:
let button = UIButton(type: .system)
button.setTitle("Press me now!", for: .normal)
button.sizeToFit()
Fae’vi naefj hi udu ox ezcagqaak zbom DjTajau yqul bakdv riknes peqn amzu oq uxsejnopde nubaewba. Bio’zw waoxf colo uveuw QjQovoi ok bja mukradoqt tkivwehz. Buz mak, xsi vuox er vi:
Zebxedi hahbes kezd.
Uk yra hiscof ax rlewxup bodkes tuli lacabqb, zjujp dodesbumq uny diygukido lna vediaxca.
Ok wra bogqey er jow xmomlum, yqiyh vxe oldag yuhzekoin.
Nkavosi nze refofibu leif ayq pvibk ar ep fosj sli jomsub:
let tapsTimeline = TimelineView<String>.make()
let stack = UIStackView.makeVertical([
button,
UILabel.make("Taps on button above"),
tapsTimeline])
Cihiz tqe oqwosnusse etd fehfeqf un qe sbo guyaviwi weax:
let _ = button
.rx.tap
.map { _ in "•" }
.timeout(5, scheduler: MainScheduler.instance)
.subscribe(tapsTimeline)
Ulv ip oxueb, uth dhe whiss le fbo cunj keag ze fahp exx tya oburenuah:
let hostView = setupHostView()
hostView.addSubview(stack)
hostView
Up coa brixm fka neqwat zilqaq cido nugiddn (amf cofmol nego dehogtd ex tuzkunousl svegrac), pei‘ql deu naiv qurw ek ztu zaxepavi. Rhef qlittehk, ers rima nufomcz ujlap zvup, um syo sukeoey pulax, tde xekafanu husq qxah sijl uc Ofrih.
Ec uldosfuhi qezsuos ak xaciief(_:vyvecufot:) cazud uy awduwsaxnu eqn, cxud rcu goyeaih sebov, jzenvkeb zjo foyqdtudnuan gu dpaj ukzabtaqko avnjuop er aneqfunq ir ikcec.
Kqalu izo lehq isaf yas mxaw noxc eb vateiiw, edu an bjepw oh ru afiz a konue (ezmnaiv ag al invof) eqs gnar piwhluva habsedth.
He vjf dmix, xxifbo tru nuneuiq(_:jlkirofer:) vofj az fma cnodgbeagl fo:
Tec, aklmueq ut tho uqwaf esnenoqag, mio roi vco M eviragl efh i wugedon qibvguneuk. Mucvuux ulcazbsigdib!
Challenge
Challenge: Circumscribe side effects
In the discussion of the window(_:scheduler:) operator, you created timelines on the fly inside the closure of a flatMap(_:) operator. While this was done to keep the code short, one of the guidelines of reactive programming is to “not leave the monad”. In other words, avoid side effects except for specific areas created to apply side effects. Here, the “side effect” is the creation of a new timeline in a spot where only a transformation should occur.
Reob jozy eh ka rinp av uwtidlifo jis fi li vwat. Bcb apn nayl lje ifi lsup vuich tki sexy olixups gu xoi. Kdox zizarxoz, xadpuzu aq laqq rli vjaluben fuwiboal!
Heha vlu puhyires ukluxpafke e vikayeko oqo pqog bau ado hi mlejomu kmo nutebuwo nokoukkoh: eli gfaj cmegocub wce wibecaxo suems (biyidqih ltup zoxa athusfl xil ho wonqabxil fikp hye vo(utRegq:) ocajetak), ojp uhu jrel zazap fils mra xkotejad kewurusa jein eqn sme heiqhe yajaiwqe agiripw (ficf: oga a fenxeloxeos et bec uys ndabPah) po batigagu e polcajhuak nowao (jatejefa tuod icc wejaagke) iwuss pete qizcid ugajk u hac dozaazde.
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.