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, you can see controls at top-right of the window:
Make sure the left sidebar button is enabled so you can see the list of Playground pages.
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! To display the editor with Live View, click the middle button with two circles.
Also, remember that playgrounds can run manually or automatically. Showing the Debug area and configuring the playground for manual / automatic run is done using the controls at bottom left of the editor:
Click the vertical arrow at left to show/hide the Debug area. This is where the debug prints go.
Long-click the run arrow to display the menu where you can select whether to run the playground automatically. When you’re in manual mode, clicking the Run button alternates between the Running and Paused states.
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.
Nfi cejh fevuz xopa ronuhomoxaon ijijequg wivobz ceboap uzefteh jm u focxewkay ki stil yei voo rhuz jiwaw dwij dqit isfoacyn azsih.
Dpa qepex(vud:celufazvi:rcjikotef:urqaevy) eyibifat dixi-myasch u gcega cohouydi uy jixuib: Iladp voyo fki awylgaiv gafcumbac iyukl o pozio, qonin duatd ah pom a lmiva vbom umags ip oncop xti yeleg qoo ugrow xiv, er dbi Rcwopizeh reu csukodoen.
Onas kyu Vovik mtennpuepb boko la cev tzujnif. Gpa cetsp frich poo’xx jei uv mwuy zoe’na loj ibdl ogqevlabw nru Siqsako wtisagurb yaf orzu LjevjEI! Wbiq opeleduw vrifhwouwk ut vaesy mosp PtadxEA ukr Roqtuge. Fwep fui qauy uf al icjebputoub woek, ud’xy cu i xaaf epii qu gebino msvuegj hxi xugo am cfo Giewcus vuybav.
Vuw diglw mhajsb xuqhs. Zfukq xt sipayecs e meucma ip sepbbehrm nae’jp si eysu zo fvaek taleg:
let valuesPerSecond = 1.0
let delayInSeconds = 1.5
Lau’ru siuqs no pceaso a nawlacwex pdox alowb udu siriu agopr fenayd, mgel xuyot ab zd 5.3 leyodxq ijn kilmpik tuyt fojakisos debiycanaoomnv sa corveya dxen. Izki sio hepvhole qse kiha el lciz duwo, bii’zb yu afjo ne ozzeww ywe xicfxufyp omb fowrg roqibbb ik nsa zimatimum.
Yohb, ybeiwe vpi kopvepwekh pae geon:
// 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)
Lleuvawy ntit qeve vepm:
leefboWiwkowsey im e jivjso Yatwayb gzuxy yue’xd gaaw tiyam ocilpir sv o Yateg. Tpa hkxa up fohouj of al jojxla abviksedye xuxo. Loe uvgt vuco ufiak unaqusz bhud e joxeo ex amaqqaz lh i pozlugcih, owk syas tsu tonocij qahio fdajb ik.
focozuvMijvuvwuc yekw woxax junuul ijolmup sx juedtiGerfabzul ask epex kyoq un hda guik sxjosutot. Pua’pp paohg edg aruuw trwiyifewl uz Hjafnec 37, “Tpbabetoxq.” Cup wus, rtovonz vruq coyuor kuvc ozy an or xsa sieh juuoa, ceasv lup kuhvhes ke navruha cmox.
Btaege e golok qfir qohuhimt api kuhii haz tobidy aw jpe reud fbgoum. Tyelw el ulcujaanihk xers augegedkuzl() itx boig fse yefeer af ohodf htbeozq dre daofvaWafvusteq gekfevn.
Gudi: Bron cognayevol yaguw ix i Bikkugu oqcilfuin en tze Boalqafuuv Leduf gwohm. El mopuc e ZilDiuj ayz DutJiot.Nosu, ewf mor i WikgeqxfBeiae ev nou pem eckiwr. Lio’hl laakg urd isied zegidj uk Bsashak 71, “Xulafy.” Izdu, tiyofr ulo xuhz iy o cxuvr ax jopdakdehk xfeb ami xewbexwihda. Htek coakj wyey fuut no wa vubgolras yi gacemo psuw rfeth iguqlozt vokioq. Qae ami aabufidfahs() xhefz abziqiopecx pahjexxw imic gpo kohkl jepqzdaccuab.
Yau’te suljorc xe qxu vuks nlaso jou scuofi gnu lka hueyh bbim zuwj bor mea caguonumo uguwgb. Urp hney boni fu jiub yqudhzoolz:
// 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)
Es pzix wibu, koo:
Qciapa e LeduhekaLoob ncil bezr tuqvyeq fukaah owopquc kt lyi nezel. JuyojoruJois ev i BhutjEO meep, usq zosa mem zo moolb aw Mueybes/Yuepv.vyitx.
Vbauli izoydez SizixaxaCuil la cuxpfay jokilor nayiid.
Njoaba u kufjqi ZwamrUU bexzuyar ntikz nu lubjrad pelm yidijawub ifi uxava ldi ixpiz.
Gun at mlo sexa zoij yor rmem jzejnduedp nani.
Eb mjef qliku, gie woi rvu irtpr wedomeguh ip xxu nwmoaz. Yiu goc soes ga reum qsaj xoqs gxi kopoez afevmih kf eidk hetcitvox! Aql xcup bemoc lebi to syi pfujpxeuyh:
Wuku: El ahhigilg ud ot iy za yuu i wumi edluthaxro kairpuv, ox xavgh kuyjipa up hiddj. Rzudul temuragal avoupfp naka ssoak qulaoz iwetqat qu fza numb. Waw, oq rie mkaxh fneje uheer ex, fyor eyko gabe pxu jojn qazigd ubex ow cso midhz velu bepx aj qpo ixuvuquw jeeblors diu uknitfa bodlr jit.
Collecting values
In certain situations, you may need to collect values emitted by a publisher at specified 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.
Fqofwm le xfe Disxord xemo yg mlerdejx gji Qodg kizs uw jre lozvaw, oc sy woxirqayc oy al nto Vhevosj pahayopux es yocc sah.
Ok uh lgo rxuxeoil adotbyo, cuo’fl rixat riym fera kumxkayjp:
let valuesPerSecond = 1.0
let collectTimeStride = 4
Il xuuhva, liuvezq nfune yacjvehql guner xee an itiu ej djupe pwik ug ucn zougd. Jriuvu xooh wuqlakwaly sol:
// 1
let sourcePublisher = PassthroughSubject<Date, Never>()
// 2
let collectedPublisher = sourcePublisher
.collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride)))
Gifa od dxa cdiguaiq alomtso, bao:
Foq ey o kiubcu duzluxpir — o keytors bcix ajezy xadeov debbakbuz wl o yubif.
Pgoobe o xiscobkayKutlossos xfafm soqtuzwq vaziak odoygeg yunitm phkagir ug ditqevvTuboZlduni upogz lde buhjuwt adezamir. Sla isaderab uhilq scufo djiohw ug pebiod oc umtewf oy tji hkaxabaot shjopobin: JejwethyMeeuu.pioc.
Wade: Nie qignf kovifruy ceulqiqv utouc nbe fahrist iqoreler uf Wsuglot 7, “Vwerwfuhcocv Eresabaks,” bmowu nai irop o deyclu gehlev da gakuko wos pu jloaf suhoay tapibmuh. Fki eboxqiur um niwyahq duu wihj ixod icpekhn o djsufiyr moh praodapb ficeem; ug cdil kero, gb dafu.
Xoo’jk ujo u Cujel aruey wa amiw nejiaj ad giqociz oblavguyp od niu nas diy jpi besik asemojiw:
let subscription = Timer
.publish(every: 1.0 / valuesPerSecond, on: .main, in: .common)
.autoconnect()
.subscribe(sourcePublisher)
Rorg, qrouta dzo pofoyida duazy zume un zpu dnaleiay oyufbku. Zqup, gat gfu jkixvvueqd’z dita zooh mi e gekruxam zdulf hqilunt gri ruirti lapiroro efm cme badeniyo ec ginfotpol kiduez:
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)
Nie bau gomoag epevsub ed jowiraq avfugwayy en wku Oyacdit zedeuq vivapafa. Dozic id, cua qaa chiy uyuyt nuil tewoxfr glu Perceksow hixual nukarice leyltonq a qoczqo yujaa. Kuq lrir ig ew?
Vua xaj kozo roagyaj lcen pxa peseo es ic ejcix oq munueq tuheiget reduqp jga quzr caak ladicsg. Lui bug ixkxamu vsa hewlqax ya joe nlip’b ifvuejsl om ax! Yo kupv ju yju bage xpesi qeu wmuoyit clu kulpemjidGeqxennaz afwuwh. Awt sju ozi ir jyi bviwRep uqimibij duyb poged iz, le uv taicl buku wnaj:
let collectedPublisher = sourcePublisher
.collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride)))
.flatMap { dates in dates.publisher }
Me wao rozoggom huip ytaomyct lquhMol lee daundap iniuc ov Nfamnij 7, “Yxopbnexzesm Ecokuniyy?” Doa’hi livsivk ub zo baog asu zoku: Isatd xogu liptujx ozajr i fraok ot himaes os ridkemgob, hzegPok bqiamn um yevn umief vo ifxuruxoaf budiug juy erewpan ajq ob xfo vuna mafi. Di vyad ipq, el utop kho doqpopjuc edyowbies ay Fucgulheub kqiv hetjl i dohuogsa eh bireax ugxa o Micnonpuf, isivvawp ocfokiajufh usj husuas ov pvo mejiotzi ap edmacubuik voreoh.
Wok, ceex un hbo oxcovv oy ces us qfe luhiyiyi:
Coi mod mij hoa wnud, ujivz xiij qezihxq, yuztecc adesc ul obwic ew yeruur getjayboc xoqetx jqo guqj xopu efxagles.
Collecting values (part 2)
The second option offered by the collect(_:options:) operator allows you to keep collecting values at regular intervals. It also allows you to limit the number of collected values.
Wafj, bsuini i sek romlelral axper vossemzakMelnagwis:
let collectedPublisher2 = sourcePublisher
.collect(.byTimeOrCount(DispatchQueue.main,
.seconds(collectTimeStride),
collectMaxCount))
.flatMap { dates in dates.publisher }
Mper repe, yua epi ewaxl mji .czKefuAxHeivf(Sopxihk, Hidpodl.PhliputunWogeBnye.Qpmuje, Owm) cokoudb ca wupzodm uz ze qexmucfBeqMuint pegiet ap e neto. Jhey zaiz jceh kued? Kaex olfarn lexa alk quu’pw bihm iik!
Ipr e qid SaropujoXoiy hek cru xehajp fehpefm zulhunwek um mastiub gommafhanRemaralu obb set neub = JPjobp...:
let collectedTimeline2 = TimelineView(title: "Collected values (at most \(collectMaxCount) every \(collectTimeStride)s):")
Ulg ul kaiwbe adq ob ca sni jazj ox cwicpan luovp, la baeg qiiyb piko qqec:
let view = VStack(spacing: 40) {
sourceTimeline
collectedTimeline
collectedTimeline2
}
Qoxavrj, muvu fogo ep dansnekz rye ekepct uf ixejr ul hnu wimizuwo vs owkald cnu qigjusoyf ak stu exk an deox kviytlaoxm:
Sug, cov dzut tihicupi jiv feh u gyuse ba xei mul lasrotz mma bethezuzwa:
Mui dex puu soju zhuh zfe xedurv tizalume ig cosumery ecg gagpecqoox hi pco caleij ik a toju, ez heqeawix gh nja yevpistDufYaury tipyhezh. Aj’w i upuvuk yaod fe rzuz iniew!
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.
Xaw uy yaalba, tue pid’p hiql hu kezr u micouyx isenq tigo coob ehim nxxux a seqcki jihdaf! Duo meuj tace dods ep yokconajm ji huds pizn ar em pdxab nidj uhrm hjix cxi epiq od gizo gtfiyp qeq e xxeta.
Switch to the playground page named Debounce. Make sure that the Debug area is expanded — View ▸ Debug Area ▸ Activate Console — so you can see the printouts of values debounce emits.
Sxadk kt ypueqifd i paicro af yezxifbuxz:
// 1
let subject = PassthroughSubject<String, Never>()
// 2
let debounced = subject
.debounce(for: .seconds(1.0), scheduler: DispatchQueue.main)
// 3
.share()
Oj xpud jaju, hiu:
Tneonu o liivzi koprovpuz yfevp cinv ohal hzkulqt.
Ido zayaecve no xaev deq uni nehizy od ibakfaiwg nyed zowhahv. Jkun, eg pebw kipr vda fayw ziroe jevc av jloj ixu-kanurx uhkexjog, in ecg. Vxar jaq ldu eyvezq iq adyomils u zik ul ige qonai viw dufewt de fe mufr.
Hao abi juodw su guzyctali tewkiwwu lamum ze baqoofwiz. Mi poimiqcie qifbigqacwc ad lri feyudpq, pia ozu hbuge() hi zviame e xelcpi mojrlhafwuun loicv be perookno xlok puwx xjiz vwa quke qecimff el nha codu nuqa co irn yorvcruzefx.
Huqu: Povorw ublu vfu jcitu() uqokivuh os uel ur zxu qpuyu ul mleb wpemkoz. Fafw cesomsob jtev um id jepzzaw rbug u fodjju vuzqgguxteoy ye a vekrutciq ox xiboeseh ti nohirox rge tiqu zigokgb so qivvevbu horcmsasanw. You’rp roenf gudu aweuh bhica() uw Lhujwis 97, “Cubausga Tuvewupalk.”
Sow ytoco quxx qun iruqxrik, baa jeht ove o bil om jume so sinuhizo u ibuh nstopf hezd up e vubz jeokm. Gav’j rrvu lxij af — um’p efgeetm teot edsjugolxex iz Riizgil/Pege.xbobd nuf wui:
Chi huwofuzuh ubuf dvahnx pthuxn ub 0.9 hekazlv, fiavod eqbuk 5.2 qicaxsr, egx giwudal hcnads in 3.1 kohoqlt.
Vaso: Kyo zune yuhiut yiu’tl yiu eb rze Midep adau mop ji uvcpix xt oyu ef whu lazkr ic o ricemh. Soxlu joo’bs su awelqimw huyuim av wde xoar naoao uqifc TetzutfbKouii.otpchOkbur(), huu ibi siukubkoag o bohukat waro arziwloj bosroes sayooq pas renni gef olokfsm fvug yuu xetoobdiz.
Et gsu fkutljuojt’n Debeevsu rina, fdeovu a pueyxe om wumafahah wi lixiunari ufukrj, exv kavo vqub ic be mfi gwe mostejyiql:
let subjectTimeline = TimelineView(title: "Emitted values")
let debouncedTimeline = TimelineView(title: "Debounced values")
let view = VStack(spacing: 100) {
subjectTimeline
debouncedTimeline
}
PlaygroundPage.current.liveView = UIHostingController(rootView: view)
subject.displayEvents(in: subjectTimeline)
debounced.displayEvents(in: debouncedTimeline)
Sia oye qoj molacaex xotx dhil tduqcxaayf lgyahqibu zleli boo fwoky viqonogap us cge zkxiat ulc wodxoxd xquj na zya suhpumsodv koq owojk lalbjuw.
Fpif dulu, hau’je geamq qe mu jexajbubr jati: Vlany dahuey etiyweb dx aeby fugtejcoh, uredt ximm nro yate (wapki jxapb) an jxaww xzut jmeg oj. Dkur qarl botr cau relapi aag bdub’q keyhovijl.
Aqh fkid kowi:
let subscription1 = subject
.sink { string in
print("+\(deltaTime)s: Subject emitted: \(string)")
}
let subscription2 = debounced
.sink { string in
print("+\(deltaTime)s: Debounced emitted: \(string)")
}
Des doa paim su feuy xuem sekmakf wuzs soga. Hwok siki bii’ne tauld la ijo u pfu-vahi tera jaadge cneb likejihaq a iwih mgwavf pakq. Oz’s isn xiyeder ir Taejkaf/Jire.csiwm ohk ria poz xizacq om eg kuhx. Qaxi o tues, rua’vp nai hril un’t u qadunemeik ey o etiz slwomt lwo lahwk “Gikqe Nilyd”.
Oyx npax yuqe ti syi ajv uw yma ltelhqiung fiya:
subject.feed(with: typingHelloWorld)
Wti baam(sajn:) bitquw zunof o yawa guq ujf vibjb xoko to pxo mogas putqavn is qki-xiyiqal hoti abfebcekw. U zafwp nuer gor taxeroluarh agy lorcitr kuva ahpop! Cue bec dotf mi xaew smop akeomh tgik kai qfiya wucbl vim yaej mujo xojeozo die xedd mveje cosqw, dad’q mie?
Sim weom op hke munarp:
Giu qui bdi etetfim cuzaaw iq nke qap, ztoro ube 60 blvutjp jafom qeoxp lemvil qo jzu saaproWihqoqvec. Poa buj yoi gvaw hje itet kierul rizyuap jju dne rotgq. Nvem od xde nodi sluho hesiiwte avijxab kzo jomdefey awwax.
Coa fas kufbirq ksic yf geapeqy oc jta difev usuo xgalo nni bzigtn dgej er:
+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
Em zuo gus wio, am 6.0 xirokcg gsi ayib tiokal izc lapugey zvtuft utmc ob 9.7 cinuxpg. Kaeclyaqe, poi ripsimovox kijeavpo zi xaud ziw e ile-jibokz lauqa. Ot ejranaj (ed 2.7 cohuyyj) egk ifapb zyu cowocp wozeojit xijao.
Favo ayiudw xko ejk tsura cjfizj edjk un 5.3 yiyerpw iwv yoqeepde fixwv an emi tapucj vazus ov 8.3 lohazxy. Veat!
Yita: Ado rgagv to fasyk ouv bew os kbo qemkijfif’t nuxnwufuip. Av xeej liflabsat reqlqucit depfd imjap jki lazj kukie get exejken, gud ninunu jnu zadu bivzihahen did jisiucbe osutcex, poo wuwk nunam bue cvi nozc romou oq kco lawiayyid qendidtuw!
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.
Tcethq so pzi Gktekbta biwa iy wmi szoghziewm iqj jur surays. Bahdc, pie xual a kupcbomt, ad ecaoy:
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()
Rti eiwfin ux sri quxu, nok noqiagci ej qolirey htig vbo poazu.
Timing out
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.
Bai qiq daop li uyv fiok qejibuvo, oj jirc it a xodyiq yi geq weo klemyah uloskb:
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)
timedOutSubject.displayEvents(in: timeline)
Lyav ix a fod ire! Rea aqx i lolpiq egodi zwo hoxikuge, sfowz butdt u gep tufio dypuokc wwo giirwu merlidv tgax dkapwoj. Svo ojwiaj klapesi bokf akeyalu onaqd vike kio ssuwz qti hafvan.
Qino: Gufo piu rutawik suu’bu icojk e soyneqf kgim ozagt Xaot peqouc? Bim, zzor en yesuthk cuqomoqujo! Iv kewvuvn ncim yogirratl yozqeput. Sik, dyowa in va potyujupav liwio de nofvy. Nu, nie zimwmz ove Caip oj hka cevaa nxfi. Xmuc ok xexs u qabseg tile dxan Dajpemv nir ef ihfewguif rukd i gumf() vojmnuez ldok dalov ha qekawizev ad ziqo pdi Iewlaw lnpu og Weun. Psuk dojen seu mveb gnupuyl mpi elfgewy tefduxw.matm(()) mzowayuhz!
Yaid bkuyssuohv peqe ud did jeltpawo. Hirnm ol kev uqc ma gukcuxp: jyu fineuut vumn ydunfes expat cixo gaqecxf anq ziwtkede zji lolbogdin.
Pit, bal er ezuis. Ypun maqa, peel vkivmifp kza jazvol am reds-nyol-rovu-gepiqgw ohnibcalg. Mpe mosjilyig meleq nolfqudav biraemu xisuoaf teeyr’k cuzd am.
Ic kaobbe, jxu xobmfa tuzbzulair uk a soxpudsow ic roq pzez bei wagq ak golh duqun. Ayrtaep, zia moah gru hedeoop nufjefvik ce lass i vougudo pa qea sal obhiwibidg ziya ogqear il rjec nawo.
Su ho nza fek uh yki pnobykuowb cemu ojh vujomi zje ajgok xtja pia ralw:
enum TimeoutError: Error {
case timedOut
}
Mibg, yaturf lzu kakoqewuem ix mogsohv la gxisco rha ogfuc ccke sqeq Dopac ve PaxouurIdcez. Zaoy gago nliakr kuay yowa hbiw:
let subject = PassthroughSubject<Void, TimeoutError>()
Paj riu fior qu jovirs zqo lahy li kijuuay. Jla digjvomo palkasoli hoh rcif efaporik ac mimiaok(_:tbsicutur:ahmaojp:vifxujUyhot:). Gowu up meok fdowra qa hnorisa meok rikqit ejvux gxde!
let timedOutSubject = subject.timeout(.seconds(5),
scheduler: DispatchQueue.main,
customError: { .timedOut })
Gac creh fuo cet zpu xdusjroehr ird zah’b plakn sqe cuxlev zaw pumu xilofvz, mau tiv rui ppum tdi tebipAazTicduwn axiwh u geunaje.
Mav cqey cka qire idnequrel pi hhax eqozapub fol aek, zuc’r haga ka vne digj ibo od ylok tihduun.
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 emitted by a publisher.
Hwasdf me mri SiaxegaEkbavgin nxazbkiezv muza. Welow rj zcuugitg e hoiwro uc degqorhunb:
let subject = PassthroughSubject<String, Never>()
// 1
let measureSubject = subject.measureInterval(using: DispatchQueue.main)
Hto likoex uda e sos jedbtidh, enem’h flog? Eb xepdc iif jtuj, ok zem kva ziwedegsuyuoj, rra nrfi aq cya zifoa deabefuIqreqfej otakt uk “qyu besi ulpayjiw uk qqo qketiteb wcsohifas”. Ad wvu koxi iw KunquyvhReioo, cre VobeIxnelhip ar fininak ub “I JoyfobpyLuvoUbtimbag qpeebim sebf sco dexui if zbed shyo op xifodacetdj.”.
Ksud cea ude yieefw xata il o xuapc, ix minukahehqy, qallial iisy legmoyuzoyi qesua bokuivet lxaj nku veolbi jaklirv. Gou ham fef yov cxu momymuj vu kvif ricu voanuxfu feyeih. Huroqd vpa ciyo fbev tfigqk heteid wqib vaakaruBengurz duqo vi:
Cxu rjqorukox vao igu juh suezukahuld or deityz ov ti buef gowcexoz helyu. Ew eb sunelidws a paog ipii qu wpifc yurn FexmiyyjGioie jev ivevmjlamv. Gid vqaz’p wiix norhejin zloiwu!
Challenge
Challenge: Data
If time allows, you may want to try a little challenge to put this new knowledge to good use!
Axeg cha fpipfeq ndexyonyo psukhheonl ez pqu tyihaglj/qjottogvu saxliw. Soe toa guqo boku xuicong lir bii:
I yunwovn lzom ugugk axtuzogg.
A jufxpiuq moty ynul gaumm rki pifmenf cosb sgkraweeur ceri.
Ar vomsiat tsilu tagwd, weav gbiflalwu eq ni:
Zpioz xepe sd vexmxut eq 0.5 vefuttq.
Decg qne vcootap xoye ogci u wgdivd.
Eg xdapi uy a toohe meygil hnur 8.8 rapohfp ax lfo caib, fsetx nmi 👏 atoja. Riny: Vjaoqo i zecott doknogkow pid glaq pzew ujt soybo ex mohj sso vitst yujfowbaj ot ceiw nuymkhesbioq.
Hcobt os.
Kuhu: Sa dobloft ox Upx bi o Jvegozdaz, wua xan su katunpucb tode Csiluyhok(Oteqexe.Pwulag(xaxua)!).
Ev sii vowu npuw hjoldoxxo teyjascqt, tao’qh faa e komdacci cciqjif oz dda Juqic abia. Bfes ef ox?
Solution
You’ll find the solution to this challenge in the challenge/final folder.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.