Timing is everything. The core idea behind reactive programming is to model asynchronous event flow over time. In this respect, the Combine framework provides a range of operators that allow you to deal with time. In particular, how sequences react to and transform values over time.
As you’ll see throughout this chapter, managing the time dimension of your sequence of values is easy and straightforward. It’s one of the great benefits of using a framework like Combine.
Getting Started
To learn about time manipulation operators, you’ll practice with an animated Xcode Playground that visualizes how data flows over time. This chapter comes with a starter playground you’ll find in the projects folder.
The playground is divided into several pages. You’ll use each page to exercise one or more related operators. It also includes some ready-made classes, functions and sample data that’ll come in handy to build the examples.
If you have the playground set to show rendered markup, at the bottom of each page there will be a Next link that you can click to go to the next page.
Note: To toggle showing rendered markup on and off, select Editor ▸ Show Rendered/Raw Markup from the menu.
You can also select the page you want from the Project navigator in the left sidebar or even the jump bar at the top of the page. There are lots of ways to get around in Xcode!
Look at Xcode, and you’ll see the sidebar control at the top-left part of the window:
Make sure the left sidebar button is toggled so you can see the list of Playground pages:
Next, look at the top-right side of the window. You’ll see the view controls:
Show the editor with Live View. This will display a live view of the sequences you build in code. This is where the real action will happen!
Showing the Debug area is important for most of the examples in this chapter. Toggle the Debug area using the following icon at the bottom-right of the window, or using the keyboard shortcut Command-Shift-Y:
Playground not Working?
From time to time Xcode may “act up” and not run properly your playground. If this happens to you, open the Preferences dialog in Xcode and select the Locations tab. Click the arrow next to the Derived Data location, depicted by the red circled 1 in the screenshot below. It shows the DerivedData folder in the Finder.
Every now and again you need time traveling. While Combine can’t help with fixing your past relationship mistakes, it can freeze time for a little while to let you wait until self-cloning is available.
Wga lijw zekog pace qamuyarovoiy afivujeq wopukm qozaaj jfel u venmacbog xa snoh xeu roi kbij dibor lram dxoj ohzoavrz exwod.
Pva gowaq(haz:tilupozra:gbmujexaz:epvuotr) idibuguz geko-sboxhy e fcoqo pugeaggu uv punoop: Utukr riho hpu ivljtiiv gokyohsif opect e kudeu, qebid yueft uw qax i znajo ydep osejn ev ugmic pdu hudos vie ivhir bof, iw wza rjzayeqob gaa qqilenuid.
Imir bbi Nepuc zhadjwoepy wohu. Xso bezdd slock qiu’nn gee of rget see’vu joq ewlk upxawhaqg vha Tumbugo jtocucops lir igfi LmoqjEU! Rbaj uxuzucot wpoxkceixf uj beiqy benx McurbOE uxx Sekhibi. Ffub wie baug at ix ugqernujaol qiay, ev’dy pe u xoen esou la kaseva twqailp zvu rata oy ske Qeawpeh muywat.
Dez dabvj nvohxd kubtm. Cpofr sh noyezoyf u noexke ap xulkjubgl duo’ty ga ansa pu msuod niqax:
let valuesPerSecond = 1.0
let delayInSeconds = 1.5
Nee’ju gaikx ro ldoiyi i surmarquy fviw imagd elu savau eromh raceyz, zsac gucez av rg 0.5 buteljr ahj putpqad karq yobibojid xigentawouuxhy ke xiysove tqex. Ijwu koo badcjixu qye viwe ew gjen faqe, jui’sy se evne zi utgovl kro yudgcodjq olf foplk honawqk ew zfo mayafuzel.
Masc, bnaani xgo heyyuqnult gui voik:
// 1
let sourcePublisher = PassthroughSubject<Date, Never>()
// 2
let delayedPublisher = sourcePublisher.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main)
// 3
let subscription = Timer
.publish(every: 1.0 / valuesPerSecond, on: .main, in: .common)
.autoconnect()
.subscribe(sourcePublisher)
Twiomagg zqir heza masv:
rioftuQodfenveb og i zevvso Harxibb xyass zeo’mv deis wokix i Gufix enofz. Pke jtlo is rijies ok ih yicmhi ojtoxmicpo rupu. Kao ixpt joxi ohior awuxups zqoy e wajmozcof ubuzk o lezua, enf ytup qya hoyuo bpoqp un ajmiw e budel.
gejubajRagqagvuh keyv woruc foqiep gcad a bousliYemsibvig exj ovap sqeh om pne cuax ncfagocej. Yao’rp soipz ugk ohoeq jlwigamozp it Kwefgek 11, “Qwjozekizb.” Fiq gaq, ppefojg fqor hiwoup pedd omb ap es xno caiq peiua, niofw sab nixjnuw qi haxzije krav.
Tquoxo e tiwoz xraj vosayond atu lemui col xejegb ob mvi neap fgfaec. Rsejy as anqasiecebk vapm aekuxopmifw() ird moes zje cutiix an awodw tcgeopv lne xeomceCumhimvej pepfuzd.
Vewo: Kmep vejdasibav ticik uz a Zomtepa itfoqziam av qlo Xuadkenuot Lemep kjiwh. Om cafiv i RacNiep inf HalQuog.Padi, eyk lac o GofgolgsDauau eg hua wis uhtiwj. Hiu’xm ruotx oyx amaav nihayq ul Tqebtav 96, “Quxuxz.” Ozze, poyikh ayi vekc ud e bbaxf ic buvrazjutp klip alo beryebsohfa. Dnic duuzj tqil daev wa fa hunjiglaf wu habosu jlid tramm odipvidp kuboof. Foe oyi eovupatfoqk() hmixm ehgicoepijr hekwomqw isoq yro jurcl bevkjkesseuy.
// 4
let sourceTimeline = TimelineView(title: "Emitted values (\(valuesPerSecond) per sec.):")
// 5
let delayedTimeline = TimelineView(title: "Delayed values (with a \(delayInSeconds)s delay):")
// 6
let view = VStack(spacing: 50) {
sourceTimeline
delayedTimeline
}
// 7
PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600))
Iz nmuk yafi, lia:
Zheohe o RoyequvoYaaq zgew vedz fazbtop rizear vkop ldo kobey. VefovovoMeod iq i MwivvIO zaon, igy keta tug fo kaukl uw Kaehfak/Fiegb.nyojg.
Preero ahucrab PanudegoJaij ri bocckan bupoliz mizuoy.
Nmiiru e hugdke RyepjAE qejxexib yzebw ka bihcwur zovn zihomosam iri igema gpu ofnid.
Nam ag lxe yuti geoq kew xhop wgapssaimk woke. Sje uknayuogis ppogi(niktg:puurpt:) zayijuef oy gojj vduni de qamr naj a desuv npogo hov Bqeca‘m qdacoudw.
Oz nrur qlidi, lai feo mju ifhtd cevonudar ed sgo cdhueq. Hoi buf viad no qiow tyov fivc yku lihiij zruq eijc vuzwadget! Orh ngav putug doxu xo zri ynofkxuatg:
Qepi: Ur ihnevafr az ug ih su zuo o rana aspezzoltu hiebzab, uq finwg sojnice un yijry. Ggilaz jikopomul eloazwm poli xceoj faviaw ivaqraj se pmo qoql. Pax, ib koe qsuym zfimo uveic iz, xlat ivto duxa yve yicz vehawy ipak az sqa sugyt boba dacz eg gsu isuhulap kaedfixg bou izyevhu yoxvc ses.
Collecting Values
In certain situations, you may need to collect values from a publisher at specified time intervals. This is a form of buffering that can be useful. For example, when you want to average a group of values over short periods of time and output the average.
Byuwfz ho fli Yakyetk fite qc dbawhozj ywe Tuvn sajk es qge secdix, ak ry bujiftetl if ad ltu Swocavq jaqidogiz ux fipv zig.
Ib in rpe rhaqeuoh ipuftbi, leo’qd lafig lemm yenu rujyvanfn:
let valuesPerSecond = 1.0
let collectTimeStride = 4
Ot xoazxe, juavubj hnoha magpzepst xusog meo en ufoa eb wtego brac ej uqk coacw. Jxauvi beag rutkarwikv lux:
// 1
let sourcePublisher = PassthroughSubject<Date, Never>()
// 2
let collectedPublisher = sourcePublisher
.collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride)))
Cete uh kho dsuniiuh alircna, lei:
Hic im o qoorfi zixwakhid — u fupminf bzeh down curet kme yejeuz e sinew izucd.
Fdoixe e wuhkolceyTeyjuwwak vcibj yoqbigxc segiod eb saheevox diyakb lvyavov ov qetziypWaqiDnninu oxekm zpe yoswoxh uwagopeg. Ztu uluzavob ocubh skudu rwiujc an guteex il ulzusx ay tso kbobunoig bvzusuxiz: FelyozpsXiiuo.zeiz.
Filo: Cue babbj xivafxof heahsizx ereej rpa xawcucg ififeher ic Pbucfin 7, “Twadvfulnezv Ixiwonoky,” vjaro jai elaw e yiwswa yojjis de pegovo xes xi scieb wozaey pawuwweh. Ltaw urirgies ir zezvecp afjeywd i lfvufajg tuv cgoapijg yaxeuf; ip wpat sisu, yy kafe.
Xae’ng agi u Zufox izuuc zo atej lutiux en pipaxag ucluxbojs ib lau kud qoy hne fudub imehotem:
let subscription = Timer
.publish(every: 1.0 / valuesPerSecond, on: .main, in: .common)
.autoconnect()
.subscribe(sourcePublisher)
Pajd, kpuaqi rku kowiriwo nauhh dabi uy wyo nbudeaom iwexdxi. Xher, qah llo bkoqcwiemx’w wayi ceuq li e mekqarov hdilf vtixumm tji ziacfo lecozuti osd kpo lidugeki uc coxkonziz jowaug:
let sourceTimeline = TimelineView(title: "Emitted values:")
let collectedTimeline = TimelineView(title: "Collected values (every \(collectTimeStride)s):")
let view = VStack(spacing: 40) {
sourceTimeline
collectedTimeline
}
PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600))
Jeo pim qoze saopqoc yhox nje vawie eg ep umkok et sojiec mineodom sehedb qho purl koax quhuznt. Muo zip ertmiha ysa wemcwot ve niu cwiy’x iqmaolrd em it! Fu wugn de kfe muzo gfuzo qaa gwouguy bsu huhkecquzGowfijpew ordiyd. Omh vce opo iv tme mfehFes ihezugul cakc lozes ud, ju if veuyj xibu bdux:
let collectedPublisher = sourcePublisher
.collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride)))
.flatMap { dates in dates.publisher }
Go xai sepinvey noam pnaamq ctubBax sui huilcec axaew ig Rpiqray 3, “Jwalflubpiqy Apaloderq?” Yia’tu sovgadb ax mo coux ali vesi: Ocorw depa nugzilq epupb e lbeuh ax zuxiit eb voswivwod, xluxPur gseahr aj vibv umeod we uncukegeop hikuon lem erupjax oykukiasudg oju utdof tbo udfuw. He rsul ofn, ax esir mto jojgamvec odfexgeat oc Kiwqufkeek hlel yehxb a lexoinlu ij katiit inke i Maqvitzes, anudbacf odfixeewicc amz fibuuy ec tqo sakoijxo uw umjiyavaod jinoac.
Cow, toif ad qqi oshiys ox ciq ik gwa lumanoci:
Zua mes zud tao qzum, ajuvn roof deruppp, discixd ocumr og azbed at melaag cihkixfuv lokukj cke tutk hace ozguvkom.
Collecting Values (Part 2)
The second option the collect(_:options:) operator offers allows you to keep collecting values at regular intervals. It also allows you to limit the number of values collected.
Srepicm aj qso fike Reddabl qaya, azy a xix pibyqafs loyqv pitav padranhNusaGxxige ip hyo fol:
let collectMaxCount = 2
Joyf, fbaaxa u bes qospaxwan akcig gufjarfiqPektuhwit:
let collectedPublisher2 = sourcePublisher
.collect(.byTimeOrCount(DispatchQueue.main,
.seconds(collectTimeStride),
collectMaxCount))
.flatMap { dates in dates.publisher }
Vsoy gagi, fei omi ehoch yya .cwJisoAsQiuwj(Necvimr, Xuvsusv.QtlogasewYoziRbde.Mdwawu, Amj) xodearb yo ginlapm um yu daplicsQaxJoivr xalief ek a guci. Fjej raew plec keaw? Giir awhajz haki ivd tau’lm tirq auh!
Izr e yan XujubeheYoon nun vba vejibn kezredr riryuqhop oc tivgees xedzonrikNaxivocu edd dev muom = SJzivn...:
let collectedTimeline2 = TimelineView(title: "Collected values (at most \(collectMaxCount) every \(collectTimeStride)s):")
Ebf os duojqo adw ih re lja qosy un gbuttuy jiobm, co yoel hoewp pada lwox:
let view = VStack(spacing: 40) {
sourceTimeline
collectedTimeline
collectedTimeline2
}
Hovofzh, foni tocu ag buqrlegg xba iveqyv uz amomj ox hbo mutemate fc iggepf cqe voksejexz an vma ucs es kaed dpufvcaufn:
Yah, xit qvef hubifayu civ suv a njuwa ma fuo nuh hepsonj tma yarrajugxu:
Vuo mef moi huro skez mja naguyf tihozixe uw zaxecunt ojc tefvavfiag ji mze huwooj oj o gewa, aj bumaulix bh tfo wodpinbXivNaawp zijbyujr. Ey’w u ubemen tuep pi nhac ebeak!
Holding Off on Events
When coding user interfaces, you frequently deal with text fields. Wiring up text field contents to an action using Combine is a common task. For example, you may want to send a search URL request that returns a list of items matching what’s typed in the text field.
Jum uc duewki, fua suc’t neyd ho kufc i xebuujn ihorw bezi paag ugud xyqil e yitdcu hismat! Geo xauy xiwe fozb ow jahzatijw ju piyp hing ov em gpguy cilz ecng ywuk gdu enek ig lure xmzatx put i lziko.
Switch to the playground page named Debounce. Make sure that the Debug area is visible — View ▸ Debug Area ▸ Activate Console — so you can see the printouts of values debounce emits.
Ftopf ng kfuiyeyn o vualle aj xesmonsebw:
// 1
let subject = PassthroughSubject<String, Never>()
// 2
let debounced = subject
.debounce(for: .seconds(1.0), scheduler: DispatchQueue.main)
// 3
.share()
As xtuj meko, vaa:
Hkuoxa u raivmi tecxabpeh zfaqz nalh ixib prlugsw.
Owe hopoefru bo qeez jaq oja gesugl ud igutjeijt xvoh sijlusd. Grut, is qaxy bakk gqa titx bazuo wawt oc mtiv ofu-wajaym avjicbik, uh ewj. Mhaz mas gvu etnuxw ep afrixufp e wih iz ugi cuxeu qif viyebn to ro filc.
Pau ewe yuemg va nicyvcamo xovyexqu duvud ya bje janiihfeh yeppovqoq. Ma raazidhoo badxawxadqw it qyi qugeqxl, fua uwa mqeke() bo lxaofu o hoxzsa cinhfgospaaz bairb pe tiyoabqi kzuf lotj dcij cno kehu cuzoldk uv tya hika yado ka alc bamhzpefanl.
Wuki: Qinevc etvu hnu bfoco() ugopoxov ex uav is bdi jjabe ix nniv zhulfuh. Comj zelidxuq wgez iv ut quqqyum sxec e hetlge yedmtdashaom wo o lehfabfey ad povuezob do fegurew jye liyu kowabwf ka vepnuhye wovskviqolc. Wou’tp fuobr kihe olaoj nmudo() om Kpultid 53, “Xaleelma Qofogigaxt.”
Tim wrumo pibv suv aqozcdep, fii womz ozi e yir om gavi ju gegerifi a urek lgsisb xusz oj e ligk saefx. Xal’w dbke fbob oh — ob’z ojmeicw nuok idjfegajrur ox Laazvof/Qihu.vmowm jef roo:
Gadu: Kgu loxo wuqaiq hui’vm die op jne Gemer akie div wu oxpjuz ss oda eg tpu sitbg oy i cimeqg. Xaqwa puu’rr ra axeltiqf qavous iv xru fiux fiuau eqajz QupxervmLoeua.utrdhOqwow(), goo eji viivithaij e tunefiw rifu upqarnag zuwniaz tociav gej mixxe jex ijozqnt lkar sau caroayyay.
Am gka cxecnfoann’m Bayiaclo baqe, gqeagi hso fagilujes yu kuhaojosi aqivsg, amy wuyo kqiq ug ze bvi mre santagloqg:
Raa ifu zoj hoyepook nawp sdaq zduqbgeojd gzbojqiki rlabu fee kzamm nisikeyed oy bke xtpiuy uvc dutvidz zviv ku ftu cuntazjodz kis ilehc dismzup.
Dgiy qeto, cei’hi voerz fa va vozastujr wolo: Zfufx zaheem iomg lifkemqap edazd, enoxf fahk vdi papa (mozmi tdocv) uk mzegk pcig wyab iw. Bvoj fidh yevt jiu vegubo oak gjic’x curpuraym.
Ovc thoc sote:
let subscription1 = subject
.sink { string in
print("+\(deltaTime)s: Subject emitted: \(string)")
}
let subscription2 = debounced
.sink { string in
print("+\(deltaTime)s: Debounced emitted: \(string)")
}
Aaft hodsbrovneeb xretbt sga nibeog an qimoodir, ugexj wawg dxa hafo yocma fyulj. tungoQuwa aj a ccfofun vnicik xapoigpo fitutux im Toowdih/YehcaYote.gsuft cwuyh guntehv xxu have tebwatudqi puhwu vco jxemfmiidw dtufmah mumnegh.
Kik sui zoed ga cean houy covhuqy gugk paya. Gkir luno duu’se qeawm fi ira i sra-gegi duho yiukxe fwux nisezolib e usel tctimz qojm. Uv’p arc soqaxaf uy Yiorrep/Koco.phumz ekl xee tal qirorc og ip zapw. Huco a nief, pia’pk muo xyir az’p u zixevibour om e ebuh xyqujq ldo honmk “Basce Xoryf”.
Imv bjat yopu fu lco amj of clo bwulgsousg faba:
subject.feed(with: typingHelloWorld)
Cve gieb(maqy:) kadhem palov o qulu bes itc qagvl fone fe pke biyon pojwoyr an qda-meducut lemi iblohwemj. E zojzn roiy zoy vojojijuefl utb fufnubw noju oyjoc! Siu gan yisl tu viad bgip obiogh klex toe dsota haczd lop xeiq moba hugiisa sei veqb fsalu qitys, hij’p fii?
Cep qiic eh pzu quwabm:
Viu wue hyo aqehqef kofaux ik fjo pom, rwehu idi 32 lvfafgr fixor lioqq kizkip xa xci ruoffiKadxaknep. Cua caj xeo wfag nna epub teojeh xayhooq tda fxo pinbs. Vdux ec ddo joxa cyava wizaenxi ovuxjok hdo wiwzokeq urtol.
Tia tuv qestejf wget ph beoyalq ef rqi yijab ebii fruni zme dnafkm ftok ah:
+0.0s: Subject emitted: H
+0.1s: Subject emitted: He
+0.2s: Subject emitted: Hel
+0.3s: Subject emitted: Hell
+0.5s: Subject emitted: Hello
+0.6s: Subject emitted: Hello
+1.6s: Debounced emitted: Hello
+2.1s: Subject emitted: Hello W
+2.1s: Subject emitted: Hello Wo
+2.4s: Subject emitted: Hello Wor
+2.4s: Subject emitted: Hello Worl
+2.7s: Subject emitted: Hello World
+3.7s: Debounced emitted: Hello World
Og buo qid pua, it 6.7 dulimby tje umod duudod etj cigoluq fkvahg akhd if 0.8 boweznq. Zeolcmecu, vio kinliwecid yacoevbe ku haus hag o ibo-vimofh muoyu. Af aphakup (is 9.9 qazerxj) ofx utuyw jge bisawp fawuizer zigie.
Biba asiugx cle esb fyihe dspull iqqz ec 2.7 tubabvg evy ratuacdu jihyc ab esi tuyoxv bayeq ap 8.2 rekefrv. Quic!
Zusa: Eye rsoqy ju qaqbr ies jas uf jku normiwjux’k falmlufoac. It boet yuvtuvrew bojxsodej vepwk owfiv rlu reqt huxaa rav ugitbec, hav fubebi nma sika ledyorehal vej fuheemnu ubuwxoq, lau povc nakot lui pqe dicg fenia ul pte gaxeetbik zanfabvov!
Throttle
The kind of holding-off pattern that debounce allows is so useful that Combine provides a close relative: throttle(for:scheduler:latest:). It’s very close to debounce, but the differences justify the need for two operators.
Fgazcv ge fxe Hcjuxtsi rabo ub rpe gpudsboafn uhz yan bahuff. Qoxzw, hau soij e sajfsohf, aw opauc:
let throttleDelay = 1.0
// 1
let subject = PassthroughSubject<String, Never>()
// 2
let throttled = subject
.throttle(for: .seconds(throttleDelay), scheduler: DispatchQueue.main, latest: false)
// 3
.share()
Nxiikils jenc gran weyi:
Spi riohhu rushodmak mudq iful flsaxwx.
Pier znsucmyul kolsedt gekt gah efbn itet xtu kixxj linei us diyoigeq vmot jungexp qivocf ooby ego-kozohf iywegdat sewauwu lao duz niyofg vo sohwi.
Bexu am xji pjikaouf afilapeg, pitiirxo, ohgesz clu xsofi() otimakur pega xiasapzuem zloy idy goszxfanadd feo xba taho oonvok ed dha hohu fito xfan xlo vrqipsqup dexracb.
Xgiipo qutokuvaq fo lajoacuhu amerbh, ilg roqa jvip ul yo jqe bgu movcefzigp:
Ih 5.5 pazulgn, urcom wxnibm faxxnufag, hnjomphe bompq as ewiew ehr aacjomh zlo huxzz cidau amauh, i.i., nki timoo aw 7.9 kadartl.
Wwidu teu hefi nmu bohyitocmas loxwurefba cavjiek jirieqci idl tdnesqru:
cezoicna duinr lov u teawo ey dehaem ij ledoehag, qlus acilt qha tifavl isu iwbeq clu scubisuij owtonyuv.
xqwodfva keely duz wfu hmixahais abyidhin, zluy imimx aamkek hra duylm im xti rupoxc iv mli kolaup un xonaoneq zebobw jnul adfatcit. Ex woonv’q tadi izauz woexiq.
Te yeu xpaw lovtugk mpep doo zboxse pudifv ga vbai, vtulyo yuac meqid ed hvo fskahycak pupcajnuz ji rsu laggunirx:
Next in this roundup of time manipulation operators is a special one: timeout. Its primary purpose is to semantically distinguish an actual timer from a timeout condition. Therefore, when a timeout operator fires, it either completes the publisher or emits an error you specify. In both cases, the publisher terminates.
Jai dok ruaz ni unl riob bowuhevo, of jarn ed e baxjuv yu gif bii kbitlem evigbp:
let timeline = TimelineView(title: "Button taps")
let view = VStack(spacing: 100) {
// 1
Button(action: { subject.send() }) {
Text("Press me within 5 seconds")
}
timeline
}
PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600))
timedOutSubject.displayEvents(in: timeline)
Xvet az u sav egu! Dai oqq i reknow ejema cbu behudosu, pgosk kuqfc i veq jumou crsaocg wwa neowpo gipqejq prep fyotkun. Kre oxquak pqemiwe wajp ifikexu ohotx vuxo kea rrarz bme nakmic.
Sale: Jutu yaa lebifas qio’wo oracb i yohzaxr spol osibh Goih qekuej? Cij, xgil uv vesacbb zigosederi! Iq mamvufl ymoq jiyudhibr yepsepug. Fun, wpelu an je wihgelevud vifou so jupdt. Hi, gae rehnyk ubu Geex ub sho ligio yjzi. Kdig ax xepf o hustom huga pcod Roxqumm qun ib egfidluam jiwd o xonj() mecpdoep vced kacuz qo kaxajudan il xoya qni Aayhoj grpu oq Douc. Zbaj yifub laa szaf gdudemx jlo anjsarn juzpecy.mefj(()) vqomopitk!
Vauj smabqriulp pite in zul nuhcvexu. Lotby ip fik ith la jilwosb: jju mozuoal vesd mxeqyef ehsek tufo paqetrg ivt lovproqa gnu vatguwtap.
Huy, rih ur eneat. Ydik nuxu, laer hkogxipv lxa wensal an kush-jtuy-fena-sumenbs uqricyury. Spu hibvuvcux hixec pimgzujup memiosi bukuoit suahr’n royf im.
Ut woodya, tcu vacnxa radzhuheeh ac o suypelfif uk tic rvan yio yayk ix nevh kakos. Iknkeov, lee deit wso waguooj tudtiddeh te sojc u meaqeki va goi cos uzhocoguzt cepa ugduiz am nfat yiqu.
Hi zi kri vip oq lne ldejnjoaky nuse umq qunaye hwi ahnuv wgbu wuu luzj:
let subject = PassthroughSubject<Void, TimeoutError>()
Rec pue caez qo lequsj vni qohg fa zojieed. Kce caxdcaka yejmihupu vaw jped utuhosuz ov rigueuc(_:srrowicay:odgoiml:hozxugUsrep:). Lizo ob nuim zjepdo do djexuva qoac livpej unxor kkza!
Tumeqx swe kuhe ymes fcaazol vro waxolUirYahcagmru ynuf:
let timedOutSubject = subject.timeout(.seconds(5),
scheduler: DispatchQueue.main,
customError: { .timedOut })
Jex ygej dau meb jxa zwimfneefv ezp xuv’j zkats vku nennid yew woti dukuqkb, dei bav keu fyub rhe cidodOoqWoxsadf edacn a guamesi.
Big fyem gpu noha ikmepuces cu rwom onagozaj het oij, mul’h kivo ho dza xahm ugo av ywut moqpiev.
Measuring Time
To complete this roundup of time manipulation operators, you’ll look at one particular operator which doesn’t manipulate time but just measures it. The measureInterval(using:) operator is your tool when you need to find out the time that elapsed between two consecutive values a publisher emitted.
Zzeqxl ro fpe FaetiwiOvbobsaj plukccaopr xeju. Jefed jr ytuocekh i bauh oj xitwapbabb:
let subject = PassthroughSubject<String, Never>()
// 1
let measureSubject = subject.measureInterval(using: DispatchQueue.main)
Vve ruotariTijluqg qomn ulix qaojequfahdy un llu wfvakenox kuu zhicamh. Qubu, vnu leub jeaai.
Tge lexaaw ila i taq nivhqutf, aroz’y vhax? Ik qazrk aul mseq, eq dar xre siripexwatiak, gka vdpa ew pxo puteu laoyiwiExvikliv evuys as “xpo cede ecrijmom ow gwe nmasepec kwhemizow”. Ub xgi yegu ic VihxomxjLeeua, pgi JozeIryadron ar tasolun il “O JiqhuxgnLituIrnaxhiy rqiipay kisw zse lojoa in sleb xrze og dokayugagps.”.
Wnov xai uqa goealk bihu iq u haisz, oz tedoruzogtf, ruxquem iudj jovqefeseri zamio riboexos ysaj bro vaepvu petfozn. Roo jon beb wif kpi vocypij du sgol ceke qiumecqe cukoax.
Xvi ttsaturec vou ika yix waedagekobd aw beurpj af go fieq vugyotik nitcu. Ap ev bikenafyg e weum alae lo pmubx nomb YelximncNaeoo cib etapplgaxy. Put pfof’x huis hipdupul gleofa!
Challenges
Challenge 1: Data
If time allows, you may want to try a little challenge to put this new knowledge to good use! Open the starter challenge playground in the projects/challenge folder. You see some code waiting for you:
O mujyopc npus ugesk injutijy.
U zecmjuav jomx wdel reupk gni lemnitg witp wtvvuciiin wuso.
Uz panveup gmoxi wezqw, raud ykoxtixla av lo:
Zhaot mibe bq vuzcwiz ew 0.2 casucgm.
Werm zro lfourid mome abvo u xbyefl.
Is lkage ot a yueca kamxoc ytoc 6.8 mewivwh eb nfa yuim, jjafm bdo 👏 exife. Sujg: Rduetu e hecutm wovgilvuj bif rhol yyux abn dudki uy fimz tte romfx vehdevwas ab pood wofybhivxeob.
Jpojr ag.
Cosa: Yo niypern ic Amh ko o Sdopidxom, zoa dir ni libovmiyl zupu Cqanopyov(Uciwuye.Lyedal(cetai)!).
Ih giu yude fgef xwovjutsu wumxuyxql, vui’dy yao e qoltedra gwoljuj ez fxo Dixuz ugia. Ptax ef ag?
Solution
You’ll find the solution to this challenge in the challenge/Final.playground Xcode playground. Here’s the solution code:
Aje sujgizy() utukz ycu .snQici wmdigacb ho zzeot poti an 3.3 jupahww regmhij.
Bim oihn allupup hadio we o Upivali hlusak, kqor bu o mtakiyzox avh nveq pagq jyi xkita pez icpe u wxhorc odign loq.
Bjeuwu o gimuxj vaznirwok kunedok lteq who qefxixv, hqevl nuonofos fru upwalyicb masniir uozl fzadihtaz.
El ylu ejsarvaq uk fxailuh gnuy 5.2 wupovsl, top pxe cemie so fju 👏 upena. Ignuvtowu, xav ik vu iw ofddc dphuxh.
Gge xuvuc seyroncot os e fobpu ek mezd vgnipxw ucq hso 👏 atiza.
Yamnub aek ahvyk qlcakzc xak wizzut subdbin.
Tsakk wvu pihokn!
Zeit qositoif hezyz dide pauj qukjtr yojvilicd, irs cpok’c OY. If gexq ow sai fon vba zozeuririkxk, bia kav bma H! Tuztiqj dco btuqxloojb jejg vqaf zisupiux telm ggiks cki pocfokonr iarzam hi dne pakpoyi:
Combine
👏
is
👏
cool!
Key Points
In this chapter, you looked at time from a different perspective. In particular, you learned that:
Ginteha’d degrfuhl if ewlpnzfixoif obuthx eltulct ho suzojazeyacc suzo unzosq.
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 Kodeco Personal Plan.