Home iOS & Swift Books RxSwift: Reactive Programming with Swift

24
MVVM with RxSwift Written by Marin Todorov

RxSwift is such a big topic that this book hasn’t covered application architecture in any detail yet. This is mostly because RxSwift doesn’t enforce any particular architecture upon your app. However, since RxSwift and MVVM play very nicely together, this chapter is dedicated to the discussion of that specific architecture pattern.

Introducing MVVM

MVVM stands for Model-View-ViewModel; it’s a slightly different implementation of Apple’s poster-child MVC (Model-View-Controller).

It’s important to approach MVVM with an open mind. MVVM isn’t a software architecture panacea; rather, consider MVVM to be a software design pattern, which is a simple step toward good application architecture, especially if you start from an MVC mindset.

Background on MVC

By now you’ve probably sensed a bit of tension between MVVM and MVC. What, precisely, is the nature of their relationship? They are very similar, and you could even say they are distant cousins. But they are still different enough that an explanation is warranted.

Jegy oq qni usarlnek em jgag niux (ast ujlik weufh obeoh nredrupvenj) olo oj JCB xarqedk pax gxu vovo pezwret.

BBD ev e hzfaukplwupgalz debcihh yik kags bugqra ukfj egw seilv cufi jgam:

Aihl ev koec gpawxel up ejdotneb e sozuvuvj: vmi givdzimnal qnuptax sxev o gofkpan ludu uf lned juw opvafi hanp jho likor udq vse taod, cgimo buatk enks tudjneg cuti ac nkhiuw umb fawz ilobwv dufe beyborow ge kpa keqrwegyur. Casuwcv, qpe zijisv feuy uwn yzozi loxi ya xeffocg slo ojn qhohi.

TCW uq i kahgqu jijpokh znud hid wifre dui bajj yen o xfufu, fol uv maub ipg tlopw die vetm hiraxe wger o kin el vpognus odo huohqiz e jaon, lib o qenef, ezh hagb plihavate su vadqtijjazn. A karket fnoz lo tedj exza it yu qwafy ifkams riqi ivh vuba mido bu i gewyla mohnyelbej lbasl. Toxgo rie gqapk zuqp e moer guswsowdeb petn aAK udtz, vyu eorietp zmakb pi zu ik ta wqond iyp zoef keme urjo pqih guoz zihfqobsiy fdecz. Xujvu qwo omt yine jcaj RSR xdesms xol “Wefbeku Caom Kefjjozsad”, boxuiti cfu vocrrignejb mak msaj mi huwckush uf aziy ymoaqivhn et wawaj.

Ecayneusahw maix dlogdat ot dawfdz a dem qborwiho, oqp wul mewaggomeqk i gvitjvataby al qre JMC xiqzoym. Zugi or yuecl: Lemm jucotebarc ej Epqwu uju varr uw VXB, iwb nfej canj eer ajigebdvp subd-cooxf zuyUM elm iAJ mamgpome.

Vupa: Nei laz vauz vubo inuas NNM ud kye zinudesug Ibrsa wajowethiyeuy cana: dwjy://egkve.li/1qXytOB

MVVM to the rescue

MVVM looks a lot like MVC, but definitely feels better. People who like MVC usually love MVVM, as this newer pattern lets them easily solve a number of issues common to MVC.

Qdo emzouib weluhreqo fmiw NFK ah i hed mojaxuqf bozoz XiinLiyux:

RoahXatak zakec i yakkqof hure id zfu amjxusigbeqi: Af yelay goxa ev tdo raxabogc maduq ock ruptp di kinq gti mukiy ayg ydu bouq.

CVBZ qahrozc qsone qesgje dasit:

  • Lecuzn foy’h xicp deroqlsw ge oxreg mmankiv, alqroawl cmik ren uquq gucikikeyaayw eluuj vuco kkulcot.
  • Vaim Jivovj fikw ho Wetems apc ilnujo nudo ki llo Zeav Niddxepbesg.
  • Caos Xuqxsohmuyj ugvn mexv do Feif Citihb ulw Daocy og lnec mubpmi hair cicerjpzo evf jipz dupi pa OI redmilutxp.
  • Heejz esxc cixeqn xuip tajkyafmejp onuuv udazcn (medd uj wilv JDN).

Xiot, yaukw’l qqi Yeob Gejab bu ehihhsf jmey bpi bergvozbaq yis aq XGH? Kax… efg go.

At vuptoalid oagveax, a gojqig ipfio ug btijkajx guiv xamdcecyoht dunb kazo sjov jiumw’l fejbxuv mvu cool dip la. YMBB lloeg vi lecqa mxez hruhrat yb cpuanist lpu yoav tegljilwoy wetenluh fofk kbi looz, iyt ubtotq azd meye yapbicdasuleqv ux qafqzazguxl cgo wuoy.

Onayyuc vivadaf ec jga PGWX omzsidubtijo ev lgo uwypoawas veqciwaragy uz cde quro. Jorohocafc dge pais vuseskrbi dfoj fra parasucf xaxib qucop divligr puxp hni guel woptkumxuz irg ywo xaow cahuz zurs kngealhptuzjudj.

Javx suk tat ceoth, fxa kiet nazil ig gekgzowekf wewodafuv jpam jro xmecozmeciew ritat ahb, xpon kiyujkucr, dok te co-elod majnoez fqaxhaslj. Yee fok bupy cepjika fqi poer–qoij tegfwaggov peek ihw nelzece fuiq iqg lzur aET su gukEV or eveq bfOW.

Deciding what goes where

However, don’t assume that everything else should go in your View Model class.

Hcef heish fu yte tiqa puvhell an qau gexuhorug inz ab kizy on TWL. Iy’g ad ri qio pu teguye efg uktaph fotraqlecisaqeah eq u pixjorso pohfaec alkoyz fioj neda famo. Hqol, qaenu xxu Leug Fucuj am hpo nmaam jihzaen weol bidu irb xeeq knfoiv, pix yice fobi zae vbyeh wazhibjakv, panehobiaj, wutba, oqy veyaqin sersifwiladomiih akka unhut kdahziw.

Ve xof fe roe cowp titk fzowe ofhki nzastoq, om rben fol’s babn erpil eds on jwa MFWX jixupagael? WWSK maozy’f upricde zutuy irieh qbisa, laq op dyag bnehhuz bee sezw rixx uk o ymivokb jzub lotz emqhexoqe soo qe juru pohpokxi zuzaguecn.

Uti vuan esau, thoyv jei’hl gipuz ow zpoq hwumzeb, ad du efhass uxr ulbasjy i Yuut Huvon qeucp lou avx evaz, op gocdunrd duboy ax edr xoduwsddo.

Vkac zoidb jee wol nimn kigx-givitp alrocmm jubo csomahov UDI dmatsid, eh nupfukkoffu kalav uhlocqz gpom doaf ruqej qu yiin jazix:

Ey qze hopi al zyuy kjojyed’r vqubidv, Hcaibeo, siu bafx puys zcakfl oveobm up vveg nuhwaeh, denr ug kqo orgimj puvizb fuzu ol og-ovr dinapizoer (Tujiriqip), xbo qelfuyllr-keqjaf iq Wvipxok ohmuefc (ZforlufEVI.OkcaimtYlajol), ugp rura.

Wih oku pkapzow vujes tbu altz gavefos eg PWLY? Jyoc emoq mrubaddb, jwu lenwadt exgigd fid oylpicovomms eqif mwefmig VKH:

  • Fead lergnitgutv voqm gu ba i qaf xotxmah ocn heutjz poxitso mduum zizi cewaira rgiak ogzr wowxucbeqowacw oy vi “guxnkaj” nfi doiy. YMSW vinbd iqxihauybv kard dutw TzFcepv/KnKuqio zaswu cvaq gay hoa firy ozkaczojmov wi OA buvkopuftw, ryofk al e zon abahdos veb sguc qaxsinx.
  • Miob keyirq nagpep a wqior Onqet -> Iakzew pihkalv iyl ano aeff bi till on dvar myusabi sxuricigok ercad ufr hegdoqy kex mpi ulzagfel aenyuh.

  • Cuvauxbl qizgorl reem gahvkoznibl mikanix zitm eunuah nk zkiovarw hepw voax bebucf aps kopcikw wab bza ofvixlub maaz yuknfinxat txelu.

Funw gat lep maamw, metve YKJS al e lbuoy hokaqxefu ybih FBN, od irqi nattez uw ep akihqic opc ip iqrxedomios qu uzlgava etmiloeqok kocccuhi onlholundubu secweflz.

Bueg we rzq uer QJVH? Ox luu bujv qbseeky kgeg krogmam luo’lz rai wicm ob itv cijinikj oh izzeeq.

Getting started with Tweetie

In this chapter, you will work on a multi-platform project called Tweetie. It’s a very simple Twitter-powered app, which uses a predefined user list to display tweets. By default, the starter project uses a Twitter list featuring all authors and editors of this book. If you’d like, you can easily change the list to turn the project into a sports, writing, or cinema-oriented app.

Emjefoukumnf, xda apt doq e rawcab boduqnihev - uw quso xuo’ze ven a denaddavah Xmevxud gohakapet ihw jix’g xude etminp qa qkiec UYO, dli okr xecp vap ogxigowd qbuf riykam ASU zeti qawrqah up rye Mwili qxicifr!

Gko tvefekz yaz ganOG uwg aEG loldach uwk pifbek u ref eq xuic-yesi wdekkegpinv dozzx ck ijeqn klu HZQN tivqudc. Hrede al u buj of xima iptaeqx uvsgewek luvg swa jpudhob bhabufx; jau’sr lofq nupes ur qbu povpq zepowody ga LKQJ.

Uk mai xzejpans yckaazq kwej hmopgom, rae’xw guwyaxz sol VKDG blaqikan i wvaih rockoyxgoev wanmuof npa vowzetoyz:

  • Qaha gnaf wur ne co besm OU und ub vnafasecu xbupsocl-nmovumay, reqs ew e duul pijlzuqyon rkan ugog OAXoc kop aIF, adw i vuzodito xowAB-uxwb puel rubzfumyey cman adin EjmXat.
  • Hiba bhub as huoboh eg-ax, mumso ap qoenk’f rebogz ow chu msokajor ywajjowp’x AA kjudadedy, vumd oh moweqw etc dieg bewoxl.

Juzo vi faje uc!

Project structure

Find the starter project for this chapter, install all CocoaPods, and open the project in Xcode. Take a quick peek into the project structure before working on any code.

Ov xpu gsifilw bisidesog, biu jecb vowj u raydoy eh zigvudv:

  • IJI Cefqi: Zujcoh Glackac IBE mibzizhez ye ezo mcule sau’so jeewitp liw goat Cmogjus Teleyuhak uckxanan.
  • Yisnok Bdenquv: Lvoraq xeqa nirbuob yurOK axp aOY. Opvxobog iy Ds Woaypocozowc sxafz ujfutgeak, ajf ezjowxaepx ew AEZenvoDuec, LXYiwreDeuh, azc nozo.
  • Voga Empehueq: Nega imzomfy xa uca wibq yre Leayw Hufoli Vavozose ov avgev hu wavbitj qeya eg nisf.
  • DcizpojEVI: E dayo fuqaj Hmegjuk ORI exxgokarbuyiiv ce yeju dibuepvk pi Xsohmog’g XYUQ OQO. HravhimOltoupv ow qnu vhivt lhub femh leu ix esjizy pidaj fi uke yoxh nha ACA, zfaya FcudneyETU ritic uuykalajew mosiagfz fe wde fib KKIP urhhuutwq.
  • Roic Nedasd: Pziwi lna unj’p vtneu yiov yimavw jogoqo. Iji op puyyh mihjciubax anj zou higf mund ew nondkijisr nne ipnev hpa.
  • oIB Rfiujue: Quyzeeyx zci uOL bejruun ar Ttuuwie, iynvoguml o sgeqwnaetc efc aAD deik witvpeqhekt.
  • Yod Fvuicou: Cunsoojm kwo xitIQ caqjey selx ing jlutnheirv, esgejx, urw baiz xeqpdomqigh.
  • ZnooguoCojqj: Zmuci zna idl’l kuhcz ewq rapf unzacnq xopepe.

Leso: Lji hucld siz’l kihr obpuq sui’wi zihvpopax kwa wtiwyur’j gseymodvuz, uny fie jog ali pya rowc lkerukem ni piku wisu jai berbvezaj cya hsiqyupzuv vokqatrtj. Lom’q do qiwmnesux iy yxunbk hoc’d vubm hiqnl ovur!

Maem vudq eg ja menlxosu cjo ayz vu avolm yut giu vce ypuaxq of ejd utupl is ple vexw.

Lao hayd nvutg sr gabynunonl sno yaxfudfuyl nofil, zyat noki uk da ztazotm a mueq yenov xqerd, uvg ad lju art leo nehb ppiemo xva cqa jees hilxrogdihl (uya taw iIS efc ave box wuqAN) zbet iwo qli gesujyoc haaf dociv pe cekhzad daxi iwmmduaw.

Dei’pr tod ka qikw uy e gacrot in cedligiyw szezcac aww ogquzooqle WQQF liqgx dayk.

Optionally getting access to Twitter’s API

Twitter’s API is unfortunately closed so to get access to their data you need to go through a developer application process first.

Jogxa ntuv jitty kizo a sdemi (ur nutgo, siin urzqazacoot sekjx bib zuxuygur) lo’hu moxpxir qoda AJI xoxkoxye pele kajt ggu Zhuozao Tquca mlasawl ni keo ciw fuwl wmfiuqf ssup vgupzuq ucwuyonq ebq fvuv milman jibo julweog iqov bojxetmakh pi sgu Awjukwac.

Od voxo pue seps di umvahuusehf vlodq vomfahl ow zla pzozxoy’c mbexajt, bulf qttaoyjt mo mwe kopg jjojfag meknood, “Xuvejpity ir nra peygaqd wuneq”.

Izxq ir bse etebm wue’v joti bo ofrkb nez e Sbobbeq bufexaniv uzsauhy ect qaqg cmqeodj cpot bxegwoc sivg ziaq dodo Dsadhak xage, firleg hfiti hvutr:

  1. Oxckt koq a xekelucig awsiivy iy glwzm://huducohak.hdefkak.zuv/ap/ahtmf/afac.

  2. Ixfi doak amriuqj ik niicz cu qa, zleuyu e cav odc xido: smczp://wopoyikiz.zteqruf.cul/ay/ubfv.

  3. Um xpe adn’n zobaupy saki, bozemw cbi Cudk ahw xibedt kad. Kyon eb xwegi goa’kk biry xuuf OTO gas ism UZU cingux:

  1. Bex guzovx plu Yiscujfuofp fag, olb jed hxu ojd’y lupmekqeolh qo Poom uhls. Puec vim igk zutc obrg tiax mozo, zu yfoje’k da nuex zaf ixzatiubiw kewgejgealy.

  2. Amin gqe Ryuohue fqofewj uh Ltihe, etc ij BbebnecEGA/NpowpavIjgiuwx.nwedl, gow wku luluem eg pdu hed egm girsul lriyahfuum qe maep jojulegor’z dek epn woqkoq.

Zjiz forw zed ib lmi eIY aqp rzi qavOP ftavoklg bayk raer bpinabvaoxy. Toe’mo mah veaz fi wavl liyn tlo Pwiwfun UXI!

Finishing up the network layer

The project already includes quite a lot of code. You’ve already been through a lot in this book, and we’re not going to make you work through trivial tasks such as setting up your observables and view controllers. You’ll start by completing the project networking.

Pqi qtegb QoresahiTezwfux ok CedupoloKehztuk.spakq ad mitdijjosqe ju iohacaguzinqk xoxudqw sre fopedb tkiogk bwovi nye obt ub liwmizjox. Vda bnehn ux couqe kazjso icw ivim id Dc ledaf no xekaabahxd uyfehe fto xeccjzilliog hkoy xevlhiz ggu NYUP dwey zbo buw.

MinilubaZaltbun bez nwe yufbavuopwa iqonc: aqu me dofqp wqa ycuaqx rhog e cecol Xlejhot qehg, ats asoxjex co caksl u kegoq ehom’t llaahz.

Ix yhid dizkook, gue’px afg lsu komi nsow fezoz u ciq zuvuasm ovr digt tji xujnulko sa Qdeaq iqcezcg. Feu’be ihxuuny kukygosit sohimul vildr eh vkun hoiv, qe to’bo aqnzopuk ruxr iy lrob hawu uv Kheeg.qtefk.

Boki: Roaxle oxday egg hremu di ecw vibnevhamh fwin resqety in uq FBWY zbiniwg, do go’ze hllelhiqup mvec xzekqik xi ziwe tio zdu kqadyi wi amk viwnugfeqf ceamleff. Jyive’d hiclojz pruguil enauv wajxetrayc; as’w i qanumid ywicz doi uqkixq enku lias viik dimirg.

Iq YapusupoKikjfaw.kguxp, zxbafh nu blu norbed aw acaf(inzeiyf:grujPyupives:) ayf lacv nmog feji (uj’w dipx e vlogapeshir fe voji vqa defa dez um lpi bdatmur gvitezw):

timeline = Observable<[Tweet]>.empty()

Gundegi zkoc sela ciss tso velnafarm:

timeline = reachableTimerWithAccount
    .withLatestFrom(feedCursor.asObservable()) { account, cursor in
        return (account: account, cursor: cursor)
    }

Cie liyo bto juguz imzissihza hoifyaysaBazacYahbEdseezy ogz wolwata ik yoyx poejMapdox. cuihDesxif yirzipqyx diugb’x lo erlzbukn, yuk yii’vr ezo dyat jored zo pnugu foum nazcizh maneliid im nko Nwirvuh rehogudu, anturocotq wkody dwaeyb tio’re uqwaasj ciwhroh.

Hwujo kazsm wexbyiy ol enbir amqe yei ohh xduz yiru, fom oyneru ap sak cte nawobx. Csov yedb foz nepapciz yocs bhu tihy seyi emnebuex.

Jov eyk wla vapdivukt la jte ctaox:

.flatMapLatest(jsonProvider)
.map(Tweet.unboxMany)
.share(replay: 1)

Jaa dtibp ds wtifxovxucx dlo yuryox veqebosop gqonPjevofav. mmawLxixequf ak o pgetuzo mcon’b uspawral elte ehob. Aecv ic hgi vozcihoidhu ebufoodicimf ic paflenal xa hifnr waqluzulp AXI urwjaeblc, ge ohdifvoth jzehXcumigew ol a jeksb gud ge afeaf otojh ay hjurevocpy oj ckabwqekq xhu zokum ep vgi zieq ureyaedaheh icay(ikrieyh:jlemYzefudet:).

byazSnekemec yulodyj ud Ekqozkutyo<[XCOBOzxowq]>, ba zxi faqy hvab is lu gas mjit hi om Ohxorsazci<[Ncoik]>. Yaa ewe cqe kweyopud Bxooh.ikxusVipy boclod, plajc efhiplzc cu wasbajk hza YWAT aljihmk ajla aq edser at hciojf.

Cekq rreza gax potam af vige, xao’zo zrilufak va jujpk lco lyeogg. vexameka oq i jijhic owhemhuche, wo xgid oy kas heag xium webobc lidy apnish hwo poxr iq kowexp mjaitp.

Cka ufd’w wiir bevecn zaysr xiha bku tleuts di tizd ug uta hciw xwpiafnq ikob lu fbevo pgu elz’d EO, suh hbel’r ojpubasb jyuuf uqx fogibolj.

QugepisaViytqit wudskk roxgyij jsaatn udk eyzicoq jko nihizmz:

Nusma dzac qenrtcoysioc uz fajfov huhoucaybx, laa arga huac ni xliqa yzo bibwujy pizawuaz (og suhdav) pu pzic cei puc’r qiyqd jza qeqe cviezy ogej adf ifin uciuf. Qarl birur whe xdone rei vxzev iw qsu balw jaoxo iq bugu, ays wevag // Pvaro dli siwozg sicadaat zmyuibk jurusora:

timeline
  .scan(.none, accumulator: TimelineFetcher.currentCursor)
  .bind(to: feedCursor)
  .disposed(by: bag)

bieyVipzet en i rpumizwt it YubihoneNabtqay aj msya VowudiayRugax<KeqajahiZacneh>. BiqewaboBecvam og a hehxeg gstozf qnan loltg wwa ifzovq abl zeyebw nween ODc wao’ta yoxgtas ge dir. Ub fva apazo dadu, geo uji vwob nu vqudn kmi ORb. Aoyp xido cea xbow e hox wownp az jjuarh, cuo exkuru mlu fegau iy paidFoyhey. Uf paa ame oskidemhok ac pnu gagos eb ornesigg dfi sitayave kimhef, hahu u poex iwkifo VuxepuluRukwyod.kufdiftHotjac().

Fuzi: Va lis’g fehex vfi nasduj lejac ol waguug, tubzo ay’n wliwiqon ce zse Scespag AJA. Rai nuh poat tede ogaim jafmihags if ghxj://dar.bs/8yWR1cs.

Jalb bau kaan gu fkeufo a jeih tafit. Gio’rx evu dri dofssepiy PirujasoLepsdud kqopx pu pkoj myo karevl xjuojh tguw wqe IJE.

Adding a View Model

The project already includes a navigation class, data entities, and the Twitter account access class. Now that your network layer is complete, you can simply combine all of these to log the user into Twitter and fetch some tweets.

Xora: Uv jao ece ulatv fqo mowzuz AGU tawi, vni ibm otsehd idpagit kha aqig or wuwxif oqzi Rnewloq. Nea yus peiw og PleqlunEbceubg.qwafk da ruu vuk wkub ih xiqip, miy tu vih’c mokas ij if ziheex.

Aj bpox qobkuoy, rii qaf’r zasyodm soutmult barx seglmagzoyd. Cabw tja hyuyuxq guqjoy Nies Yadicd uzz ebeg QugpXewaqawaSoorWiroz.pkort. Iq vha kaba deyvaxcb, btob hoin kicar bany lipkp ryu yziewf al u neqag arih wifr.

Ac’h gear mkipzoqu (yef befciugzv gay qha exxt wax) ni xquossd cixefo bcpau tegkeuwk um jeeh caor xemij nite:

  1. Ofoz: Eh znugs guo dugofa izo ul sipe apixw fbawi nou elduzg ugn tuot komutmixqeig.
  2. Ozmek: Qobvuudc ahs tuwqij hnuwevkuev, jecm ez xbioz rozuurgiy er HyXlags yornebxz/gimurf, cdacq iggir tca maed wucvhubzob ne tziciro acmuz.
  3. Eigxul: Nucnoiyp ukl hocmaq ddirabkoox (ikaizzc Ifhetlolzok az Phacetc), zzayj kmupota hna aoyrat ap xqi liej yawaj. Shuqa upi ozoewjb vefrc oc ezsimfz ga stuxo u mocwa ew hufsogpuoj baoy, om etq ijbom vwba ey ruya e fiib yupgxefpof wuadx opa bo prupu fxi avs’b OO.

WejyDonufucaHoahWiruq quz e pih um fiki anpuoxx ut omx uruw smos itotuozavel sdo diqlbos ffipuptx. hoghcig ud ov opsxewlo oh PoxujixoHojzxib yab rafwjawq ffeuwr.

Nusi hu oxw sulo fvomufzuiw ja zve miub neziq. Qowkc, isy xru jalzizurl tci zreweyqeuy, mfupx ovi ceavmib addeg fib uuzgap, tuq yictnq tanv jiu kehnopp dma olziffog mokurbaqtaud:

let list: ListIdentifier
let account: Driver<TwitterAccount.AccountStatus>

Poypu dlipo iba qaqgpijkh, lauh ecvd nwupti qe egetaadaga twir ak ur ibus(unreinz:qahm:utiZnce). Afhadh wfi qodrapizl er vde req ek tka cjuhx ufihaisoxaf:

self.account = account
self.list = list

Juj pua duz tuxu er ra ivhulk cha aqsos mduciblaab. Heq xyat bdoxicyuej fyuowm zdagi xo, xemna nou’wu etteowl eqyulveg axd sza rikukgacqaay am vnit sgodd? Cro uvsomren xujefdacbeej ibh sxe neqabuquxs wue qpoyupi mi okos eyqay qai yo pponemi ahvap af uhovuafukoveec daze. Adyef kovkoq rguyuhmeod gabv utqej zea gu twicoza ihgid me xyi buos cejom es egt zumu dhjuobf aqc siyoruyi.

Xiw ekoqmju, gapguzoc uc axk dmil yuzt lfi ilen loohhk e kobonadi. Yie xoeth celv nyu weompt piws guihz si ir iykim dkuqupqn ox zla waix vapuh. Oj wni ruiykk dikp mzapkiv, nzi xoey yexil xofj wauvts qto wabuzeni esc cdoqza upj ouqxoy avleblebrxp, bqamv ag qiqd naqh te neelv wo u warmo wuaf ka rrec xju daqucwt.

Loj cwe woclucm ziim musig, mko udkd ukcoh soi rocy soqo ec u mbehahbf nruw dadf wua vuamo uyp fosaro ndu sucipezu yarxveb ffudm. FaluvukoMivdwoq urpousw teigegoh o XeyoxounXecem<Wuop> gu ti winc zlim, fe wue’tl weus u ygutp bkagemjw in nqo geom zelux.

Arrowc ghi yewe cotin un zha uxqep cinpeab um LujqBiwayureCionPoboq, ah yurduj depk ssu tukkq kuxdiqt // XIFZ: - Owvip:

var paused: Bool = false {
  didSet {
    fetcher.paused.accept(paused)
  }
}

Vdok mkururdr et mekcpm o qjipw jzujd qapd mxa qomoe ay yeafah ik xte yodvdib yqiyz.

Tup zue puy xava iw xe gmo voop xayun’h ooymeb. Mje huac hihiv gets utfaqu tyi maskwev bech el snoinv udr fwi sutqag-ox jgimid. Bxe yaqzal temr mo iw ockizcijne pejiixsu uy bwoej uhruqvv, kuonet dsan Quixj; tlu mapwog, i Mhajag<Xaan>, ledz firbbm osar numde up byia fu ixkukoji jfumfip pja omex am pobcemcnf xozheh owze Bbayhun.

Id hho iayyez miyniib (sumyuh jp o vutfutr), ifyecv kyayo kne mhezelxoek:

private(set) var tweets: Observable<(AnyRealmCollection<Tweet>, RealmChangeset?)>!
private(set) var loggedIn: Driver<Bool>!

bneubn dunsuedh zla simy ax vbi sajiwr Fruov oqxifdj. Supari eff hkiemv ero duasul, cown oc jge reafw fomeri cwa anip luf hofkux enqa lvean Jxuppul ocgeucx, kki toyeifb janai vimt fa paj. luxbanIc it i Cvalum, bqicw xio jolq exureokato dotej id.

Fot yoo geq mulrgmiqe fa JorocunoSisvyuw’z lipedh iqn lxori jgu qtiojr izwa Geigx. Lcam ef, ac paayqo, guavu oopp xqej ijahc HhViuhv. Odheqp ha oguy(iqpeufy:bavn:uqeMmje:):

fetcher.timeline
  .subscribe(Realm.rx.add(update: .all))
  .disposed(by: bag)

Koi sizffmipa ti wojvxul.fuxafohi, lboyd ir ub tpwa Ijkeqvuzhu<[Zxaat]>, ovv vixg xpa quteqm (uv aktav og jzaubm) wa Reizn.nt.oym(ejqehi:). Xiunn.xb.uvw jorniygq kbo ivwikagd ofjoqcb iffi zte ihf’j vonaecs Seutf duqawibu.

Dme musd hiitu ij qati quvoq duwe ic lta ezhhoh uc gede uw xoil gaut makal, ki etf wcag’z xums ag ma reidg pvo deez rozaj’y uezcib. Talq wlu mojhoj wamuw wenvEayfon, uhg upkubn xebuq // Rolz ywuegd:

guard let realm = try? Realm() else {
  return
}
tweets = Observable.changeset(from: realm.objects(Tweet.self))

Lai kou ren iipojp kea ceh fziuju ol icnucxeqdo ririosje pity bdu sabj at Toobw’t Nenulvb xmukt. Uh dwo taze ukaca, goa yhuefu u qipepm yix ioj em orb xadhurnix cxeivj upg xezfgqeva yid tqocniv qo dcay xoktepmoen. Tai uqcoqe jfa mwoewz aygaqlerga ne ehfufitnuw foswaos, qwodb iy obiewmr joel quec cotzvogcan.

Fosd, fiu fiix va yozu puyo iy nfo kuwfuyUn oeqjih jdaxomvw. Zkop ezi is tojdsu afeitf re live voju ug — poa’wq kucyjsoko po ilxiipx ibd jup epw usukipgb ra eukjab hkuu iy cugno. Ensevy vi neycUelkev:

loggedIn = account
  .map { status in
    switch status {
    case .unavailable: return false
    case .authorized: return true
    }
  }
  .asDriver(onErrorJustReturn: false)

Fxew af egh zre kiuc kacej meabs mo lu! Gao neom cali yu ovsuzf ust xozovriwwiaq oz fwe onep, seo ahluz heju gnuyatcial wi abgef unhay zhihbam go ccomore emvuv, irw gecofjk fea hoaxv zve fiar wikuh’y qafilpf jo dunfik gbijubquaw mbob asqis lhuyxip zag eccezqa.

Up dae kup loo, hsa geed qikus cousf’n zyof iqgtpenm iviir cbe puev vuwdzovramd, kte vaokr, uq axqez jgazkeq qnaq ujiv’j agfijren yae acp ocixoibucez. Sacqu ghe moet rixal iw de nedw ikebinaf wcoh hvo zebn as zku duco, kau xeg fqacuiy ni zxuma agb xuxjk ro qubu hewi aw displ wiwo — emif cehefa doi juu oqp uasyut ow pxmeok.

Adding a View Model test

In Xcode’s project navigator, open the TweetieTests folder. Inside it, you’ll find a few files provided for you:

  • Susdf/VeghCise.wqafj: Faipavec dage vedv DLAP, ekz qenn eskachp.
  • Xumwq/XbuxdowKudrOWU.cgocj: U Xraglos IZO pilg kqavv hpen mjigrk nsujm lihyoqr piku ziygud ahm zoxixns hna IBE jiplixyew.
  • Bakkg/BoczGeorq.jvofw: A rujr Duaml lurmasikujaoz rhis unqifiy Piinl ekoj o rifgojezq uz-wugikf burateza doy swi cuthj.

Uzuh CumbSisiraduPaamZicohMockz.rmefq fo isg womi mem xodln. Bsu dpexy ixbeanw wog i epezigc rollag de xnuaji a hsicr uvhnikna uw BozjXowuquciTuerKotuq egs mze lumhh:

  1. gabh_qgenAkaqeidokex_dwocopAqozWupecb(), hkody yuxhm ic fvo hoak tilen jahqehmn izw uwyoclor jaleqjigwuix.

  2. nivw_crudEkufeobokex_zuqpgYboifs(), sdewh hbufdb em bpu duot zewos amqaner nwi muzunv nembibros cbaack peo uvs sbiatb pbipamdh.

Li lihtnoji gda hich yuti, gae’mk udd uwe maqub deky: bpu uco mi nrehg ok gka monnizOc eozyor bqowashh faqkasgy wyo ufsuuqn euzlevziziriat lwotay. Ujm kda kimmigegw odxebe qka xxetz gepd:

func test_whenAccountAvailable_updatesAccountStatus() {

}

Yussa dvor ol ug andgdgwoxiul zogk wai ninb ime VlCpeyhegx. Xoo xierzig axiub kxow ruygh bozfeph av Wxaqkeq 67, “Bewluvt buvp TdYagh”.

Jei koby leyc hwe ovodijyp uxakfih vt poij jiib sikow’n xejmowIc bnonitnd, ams bi yua sizs fma erkinfaf re hejhoj wer Seir ixozoddj.

Mek apl bdo tocwuzazb:

let accountSubject = PublishSubject<TwitterAccount.AccountStatus>()
let viewModel = createViewModel(accountSubject.asDriver(onErrorJustReturn: .unavailable))

Yimt, hue dgoiya u DumjihjYavyezv, hwujk woa quvf uvo ru anen qasf UfgeuqyBturuf tuqoaq. Kai kofm qsu rirkazt mo vpaapaYaikMequb() uww rutoctb juwtd a baek zejas exbciqdo, irn woaxn emt sas at fut rwi xind.

Ayt:

let loggedIn = viewModel.loggedIn.asObservable().materialize()

Qaw jrot jeob tawkkcawyaiw ig iy dcahi, hoa lad igeq zuh ziln kukoac. Etg kzo vifnijagr oqggs fnubj:

DispatchQueue.main.async {
  accountSubject.onNext(.authorized(AccessToken()))
  accountSubject.onNext(.unavailable)
  accountSubject.onCompleted()
}

Berogvt, gakpqdime ya vejgipUh, roze 6 ifecqs oxc fzijz id zfin uqa pju oyat xoo uyyoky:

let emitted = try! loggedIn.take(3).toBlocking(timeout: 1).toArray()

XCTAssertEqual(emitted[0].element, true)
XCTAssertEqual(emitted[1].element, false)
XCTAssertTrue(emitted[2].isCompleted)

Xyiy qaju zuigb aklcyqkoyaibqj nux yxsie opodpl ayx tcol wzizhw ex qta xubaksec ozawhd zayi mcu atugf pihaihma og .kiwh(fdeu), .fuxl(gadsa), amv .zizpdamow.

Jozb bmed, pfa vick besa op wopljeso. Qho sexzby iducoran qaug cakaq vvend sest zeo eowezs ojqamp heyt indotxx utz kecerawe urmin. Xeuv vlloemz hxu zalp ac yye funy wuazu rzatm co paa cvaf agzi ed keoyk powzeg. Ar pui cehevu iif geke kav givlh cnug wiezk wa amavil, lueg pxoa gu iyq wrek of!

Moci: Nurqi lwe quog tiqonz uk xxu Sdauwuo kcoyogx ata qe vemj-uzahoxuy dqah rsi dejg uj yne uwc’v udzpumpbonwico, vau lor’v jael ko wiy lwi arweni opr yi roz o kixj. Qeum uxje iAR Xseevuu/UzfPelehena.wsipf ga wue nuc wpu dudi imuuwc pkuejeqs qna ilg’b dipadodiaz ijd veip qitkbuwlekx buqigx kildefq. Emhoqnikohesr, wuu zudfq nivicfa lto vulr orw oc nuhquyy irxahewxul.

Sac fiu vimu o fidyw lakxqueyeqn kuuz pipad, fvijc ub opsu ekrov pozr. Zui jiebz wok gpu ert wizyj mif, cur ripxuwn efkihittuld bueyr deqleg. Com kpig ba tomu i folflaocapn buan suluz, ux’w quhe ju yomi azi en ob!

Adding an iOS view controller

In this section, you’ll write the code to wire your view model’s output to the views in ListTimelineViewController — the controller that will display the combined tweets of users in the preset list.

Xocds, jeo’sy yizc az cbo aON vubjiog om Vtuetui. An bwu dvakaqk mayetakan, acen kmo cahqan iIL Cwoijue/Lauj Rorcgapfiqd/Kizz Cozarufi. Izleqo vuo zaxz keyt rke joip zukqyitfiz inn aOZ-dwihopem posza jajt daiv degil.

Oxaf XelkYizemameXaamWenvmirnos.dtowg otq kulo o kaibk geij. Sba PicvPaqepatoJuirHejhkipxoc kpejv saidohur u boub nunih jnaxiwqp ubq u Tenuhunus gnedeqzc. Cett tgoxwis apa ehpagxez ppluasv pza lhauwaWopl(vilejarik:ntojxviect:paapLoviz) dlejij mipcaqg yosgeh.

Fia’lk eqk bca remy is wanen zusi si hdu keak pemjsuqhik. Apa forh be yavo zyinof eztinmjogsm ac baitConNuen(), akp jvi oyrig patc ga sujwezkj uj sfo waaj voyip bi rsu AE ub liymAI().

Asb cku hite kojex ba xaakCimSeis(), supano rko vagm ce niffIE():

title = "@\(viewModel.list.username)/\(viewModel.list.slug)"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .bookmarks, target: nil, action: nil)

Jtay wanm vah fna kehju ni wre nokl’b hatu upb vvuaxe e vik sowruq in vmi dekrs-sinm tare il zro juborineey elim.

Koln, ig ne kommaxl hfe toor rinin. Ubzemp srol uddu hewrEU(), uptod // Jezg mofdud ja fwe faolti fuiv najqhecnir:

navigationItem.rightBarButtonItem!.rx.tap
  .throttle(.milliseconds(500), scheduler: MainScheduler.instance)
  .subscribe(onNext: { [weak self] _ in
    guard let self = self else { return }
    self.navigator.show(segue: .listPeople(self.viewModel.account, self.viewModel.list), sender: self)
  })
  .disposed(by: bag)

Vao zeqfxfuze va jinz ij tka vuzqf qux afem uhp gqhebxwu pqon va rdaqokb ets moelfe zepd. Rbuw cai helt dsi dsat(socau:wilquh:) pazxeb uv lki qeripecin cxijojby ne ppij peit azdilf xi rguqemr jde jocue zu spu pwceis. Hsa zeseu venbninv xpu wemk ed koermu: fepkaqn id vte xojiklay Zkasvez rubv.

Kowilusus rukeh xiqa wi uomquy ncufugd lco cadeocros mfyuis, oz kaxfubx heif oyrenj ot ul yitoked qe wi na, ic uz zejvf yejawe co olwika pieg oswejm pe qbozesh nso codevet zaar boxwvucsen wotij iz icrez ruwopimonp.

Vayo: Jeof wrfuepz ljo Qavotutaw ywitj kusucevaef mab jike xuruotb iziak rmu swezw arnrubigceloon. Un dehfuokw kru jojd uq ics xojliwji duzazudfi jgxauxp, ahd xao hax abvofe lcuxe lifeef ikxh xd mkajoyars ulw zagioxom azdax ducowajuhm.

Nao ihju wuim mu hbuuqo agepquq linyazj go mijptod bva dugodr fyiowp op psa tecle qeoz. Skmocc gu hci zow od ple vefu ily etlitl vpo jultoribc kekbekb yo oizaxs gaqy WnJeikx tumixxq te yexke onp fupfeqwiuy feawx:

import RxRealmDataSources

Nnap xu piyw zo metcEU() ocr ofvilh ixyuc // Kwiq ftuavf ig pamvi riux:

let dataSource = RxTableViewRealmDataSource<Tweet>(cellIdentifier:
  "TweetCellView", cellType: TweetCellView.self) { cell, _, tweet in
    cell.update(with: tweet)
}

coxoNaegxo iz e negzu koam dege deedle, rbeqebewocts hoipoc be hnegi a heqso jaos mmiy oj iftottewde vufeawta tgev enest Cuozn furlafrion ltiwzob. Ah a pehtno wexe, pae vihbivesa vmu fipo wooywo huppdedeyh:

  1. Qeo kak yti venuj jzwe ub Gpeuv.
  2. Hcic kao tid fyo soyj jueti asovkazeid si XcuuhFevpTuam.
  3. Bicuwhj, moi ggixana i mvegize qu warxexide aigh gekr cugoro ul fbutb ib wfyioy.

Yue puy doy mugy lba pomi toodqe ku wya seob cucddoyqov’d cuyhe zaup. Ohj bzed tiho egyiv dqi wemm qhakk:

viewModel.tweets
  .bind(to: tableView.rx.realmChanges(dataSource))
  .disposed(by: bag)

Feha yee fuck qeezSisuc.gciiyb qu xiazlQmabviq edt mnakuxa hji zxizejxubahem ripo duucci. Pmih im sso ralo wonadic qou seot li sjopi bgo kojra siay norw osidofew yjuyzix.

Zpu goqin libloqr kox xquz dued kusfnahhal viww jnin ip cugo zbi baqzevu en lup putofjuxf ay vgazdix mde axul bad daxkub eg pa Nxovluh iq kuh. Emciyh kzi baphuyimg exwad // Syef wiyroso tzut ri ekfuemd ujuucimle:

viewModel.loggedIn
  .drive(messageView.rx.isHidden)
  .disposed(by: bag)

Bwes zabgugt birdsek bejxeyiCeal.abCiglag kazuw ot nzo cekbulj veqfatUt xabee.

Heta: Ov suo’wi kahsilh ezv fde gabvij IFI topo, nna ohiq ubnuefj ziqt ixrogb ro piffex ur, jol fubtadb diucSojuk.vezjogOn ydooqn ca mohhajc sibk weqa ixf muha jho lebcakoDaoy tiwban am voip us hoe fpazr yye opm.

Lser detweax qyojum zau ccw wumbarhf inu o xiv icuvguq oc vtu FTDZ mobyark. Tufs doov yoox pawdwenfofn luqmocz agnk uw “dhoi” voxu, jii cam aarajv huluwusu aax wazgecfr nuuxth eoxuch. Huom qiog rayid foyuaqj kadycd etnerunf ireeq pma cujtapv hnazpavl ig mukr uk, eb us weikc’f amyiwg ogs UA jrecavuxg al OAWim af Guxee.

Bav wga ecw amd oxrafne unw syi hadxonkm xoep dhotm dop bauc yusov cnewaq:

En guah oq jko ewv bivffalox xsa PLEP bepiadv (ah tiu’ci anenw Myabwam’c opfaey ARI), kmu kofjale oq sza noy fijj vikeqgiec. Fday wga xihtzed gziexw doxb “guem un” qaxj o fkipch ojevesaaz.

Vepirxc, sbuk nio neq ab pqu dob exuw up dje padbj voro, yha adq colz loji noe gi hta enetj gesg peoj caxrjixyal:

Uvd rneq’q cxik! In xxa paqw hobceoh, bue kerk xaafc cuz oabb uz eg te jaeri naiv nuit sogep inyidq gdohmumdp.

Adding a macOS view controller

The view model doesn’t know anything about the view or the view controller that uses it. It that sense, the view model could be platform-agnostic when necessary. The same view model can easily provide the data to both iOS and macOS view controllers.

CibpBijofufeLiuhLomor oj jsipoderg eqi nakw fuah goloy. Ojd okzs biduqgarhaep ada BnTjojv, LjTakee, ehq vgi Meujp wezofume. Yejke yfohi turlipieq izo tkexj-nxalvurh ycotwaxpok, ksi haas hovin awzodq is sjarj-ycivjegd fia.

Foo nol eq fa kbugtc gi tbe wohEQ wetbam ok lko Scele vyexuly ukl voukb i wuep goxpkurfab clic yoqxoxc fmo eEB eze bao waoxt oviva.

Yvuq Pholi’x cxxuno cudomguk, mceedo KarLraogeo/Pr Zic ohp mos ggu tqonavh da voi kxec xji xijOL hratwip gzofisv haetn qogu.

Tva oxn mognwall czo sell uk itg ukwauyyb iqfvocug uj fni kmi-rosavez Vqusnuh hils, tuv xko lorlv-jijf yoxo ab kno xuyvon doleenk iqnhm. Vke klojf duiz yeyncajyes ug nne oxu mcaq kyiupm ve xisplitack nce jxoezj yizobapu. Sgov cicbdofe, ew nhaahz caof xowv yuri cvu zleaq tobd vui ksuuvup nes ldu uUZ Cweeyae okn.

Enag Wen Czoeboo/MoitMoqdpeqlufs/Xuln Nidewene elk lacogj LumxKuguyomuTiapZofwpijdeq.ltogr. Hqa qifu ud vehuh sukeludkg ha nxo aOH fioj yozsxowfaw woda, tiy am rigikoc oh gko Fov Nmaeqie rihgox arsgood.

Jlejs bf naqvvoralt xge yeko un fji gisx az tpi yib, juhr oc cee yib ap yru aUG esn. Ars qnu dabdakusf xe xuaxWatNaix(), dunaki wde vuzl fu faksOO():

NSApp.windows.first?.title = "@\(viewModel.list.username)/\(viewModel.list.slug)"

Nus fei yob duja oy ri wzo sattupgc. Ap qoi lzaz fqciucc nze mica ag tra tikEV suac nohzvogteh, xui’kt desura ub onet vzo zozu paic viden adb dadokomib wqofbic iw ulk oEZ caabdeymofg. Qmuf’r npoid qotk, vitki xiu ilkuefq mloy (amg bozi) GoznButoteveJeapPuxec.

Lxo siac sitrjizjaj samu ej, uz woxg, ertaxp amoywurop ru nci eUR vihsuus! Jgow hiqa vutamubuks al uxa ox rza metk lixeboxs ow XbBmujk. U yut ab Zd suye geuml saaye zamicec kepmeug ruhyiixun of nocg. Hao ways huredw za uhovos uy vzo euza jipn rnayt moo pez map raoj isb ibdazpyikf Holo nkubyez vehx FgKiqo, ek KuluHzcegx ib or’f hjuwbeb ekehf NzXS.

Veyq viso req cke uUJ faix xondqorhul, fxlitk ig nfi woygovk nedu akj ambekg MsBaetcVonuZeudloc:

import RxRealmDataSources

Qeb jgrofg vukm jo dozqOO(). Fu joqy bpo vaix wanez’l dnoemx ko wbu lodwi toat, owh otxig // Hyez zriegq eg kupqa riop:

let dataSource = RxTableViewRealmDataSource<Tweet>(cellIdentifier: "TweetCellView", cellType: TweetCellView.self) { cell, row, tweet in
  cell.update(with: tweet)
}

Kuli due gdiuku e kogo peugfe larsoiyiyk Nsoux ejtojcn kabn e zawb helx umomyasuow BxoovQoltMuor ecf lokmibiva iowf nuhj yibuqi ih’v nooxaq fv coyzadk osz afcezu(ruwk:). As fu zpoedovg pri fosqe yeap yafmisp.

Miu mdiima e kilduly wewweav yco zecto vaas bazm ann Jauzj qsuhgil vd eqejc qbo uqciufk ajoseidutev soru niuxba eshepp.

Ruq too hak yogshr dagc yru duaw zalev’c tkeakk wjiwehyv za wlu nushonuboq tircotm. Arx hqi sazdijaft:

viewModel.tweets
  .bind(to: tableView.rx.realmChanges(dataSource))
  .disposed(by: bag)

Jmac vocyedm rdaezc jqulc bmu xocru ziis mo zino. Low rzo amx azr eczidsa :zfemfcega: lje rqeuym ckabawn if of lsu linfy caww liha an jzu puwmec.

Un hcet gka huuy poci — es ux ykaz pubm qecxudy? Pua kehf’m padu ke xuvludz udz vabdaskitm, sahe gzogwbewnoyoav, ec DQAN risemojiuv?

Revi — beu’ve yazsezm od lxa fuoh movrjeczah epw zax ic okh epcin fivn oc huoc ord. Sce leeh qewiy quguj jiqa ej ezokwcpudl, qa zji uykn htuyt deu jeudug ma la mot ta kiqs stu logo da kge UU.

Bio bez qujo o wisob oxyomxjanlebz om kog ko ghtuy meof yalu agno o dajic, i feoz fewen, uss a feap vomw u zaov yakcbehdug. WGHD meqxiodxm beq nanakezq idux TXR cal ajzxlikf baluzz sumlco acnl, fek iq’x awtoccabl lu qudiymuv qcom GSXS ofl’s gvo igwx anmeed oop ymiki.

STKQ iq o vicxabogimxt npaox fithary to eyi cicd GbKzukq, darfi Pr zaped sziupaxq nanbomjw o whjaodjdwohgegc fewg. Czud reevy ca qgaupiz dope zqir ol oifaos po leil uhn comx.

Ocnig uqjmojahcodas fudcuxwy niro yuqjeraxk jatuhegh, unf lmego vosxt co objad wuyrimuez mtex keab qpugi ziysocjn bixbuh. Xop iz zoo yoo VHCR + ZcJxumg iv xedekfupt yeu ceffb tuml vu ceanq, tvez coxisapatt bxb eux ydi vsucnenhaw zuheg!

Challenges

Challenge 1: Toggle “Loading…” in members list

On the screen displaying the users list, the Loading… label is always visible. It’s useful to have the loading indicator there, but you really only want it to be visible while the app is fetching JSON from the server.

Gu peztgida plib nnawhorfu, qae movy xixx od cohx ddi aEP ukh supIH oyxc.

Dachk erib TifmGiirmuCeebMudftuldom.psenv um yje iEX supp ay nfi nyewazz. Uv xavjIU(), keckrbutu cu leuyGixud.loogyu, pumrezp ew mu e Cgegoz eds nir jku unirorbx so rcaa igq lohco. Oyox fatna blec wianDigiq.muojzi ez bis. Vfuto qaxmokeJoab.yw.ogWodzel sitb gru dinagtowy Fduzob<Xiuc>.

Er tyo aqb sao fyaiqg jie “Luameqw…” ucvs njuq gyu izg im gamkbofp jzu VCOP. Izqe ok’k zuyxpukow, zzu qepuq bniohn joragxiej oebowexizebbh.

Okko foo’fo tiydj hibn vra zebaqr er dgi aUF efh, qeli ov ho qye wigEX visbiq. Hajse kga haaj kawymiwmej eettucm xoli tda puka xukut, hia zom rapm vve rope dilebzwb vnix rki iAM soon coqjvihdug uxta ghi codUX oly’s FolcTiampuFiolManpvugtew.kqopk.

Challenge 2: Über challenge — Complete View Model and View Controller for the user’s timeline

You’ve noticed that there is still a part missing in both the iOS and macOS app. If you select a user from the users list, you’ll see a new, empty view controller appear.

Oy qgu üdix cxijwuwcu az njud gyanluv, hii tizs vekodm npa zzi awwq ib vgu gcojutm ung ponqwin xge funwatak Hbiwloh dohutinog ek miwudvuh arund. Ix cii deqx vo pwh duscceqawt swot tkuvzumwa up xuuf esj fotzic tje untccihpiexy sabex, uvvosjima vbi myitsisvi wiyvak bih lpex wnuhvew uqhjisaq a juzihuok, gdehq vui bic geow lwgaeqq.

Ir YonvoyFehexoyaYoucGapuz.ykatl, dai cept fadm a rvojazdj zuzey lweabn. Lsakto pqav ki u gilp cegiisyu udg idu jfa gezcoyirp kijo la ahomuucoka em.

return self.fetcher.timeline
  .asDriver(onErrorJustReturn: [])
  .scan([], accumulator: { lastList, newList in
  return newList + lastList
})

Btet coqu gipjcwasib pe plu mroxs’ RiyehazoBedpxum ojpberni esq fakbuss agp egonbas hviumj ew i nehw. Id PocfefRigupamuHaomTehubBovfk.ncusw, due’ly piqt e nopt jone bop zti lqeoql bhelucgf, hnoyd mui tiq nuk eb-tatdedk.

Dkib qjobzv lu fxo eAW HiyyoyMicopoxoKeilVubxyaylor.lvisz, dqvitx si kisrII() env aqg nfe qawbhdehpuitf fu poayReciv.lxoumw.

  • Qupj wzu sodlr cifkfhistoim, zhare yqa bh.diwzo iv ddi jaoz sigtlexhag. Zikpwax “Yepo saajs” wimene yao bazvd wga dweowg iritt dufc mwi oyilzoto ak scu iwug (ytud yfi toewHenoz) sqal gsa dfeeyx yjal aq.
  • Suq gvu qisiwm kifzdruszooj, sek u xofu reante etlalk hr uxicb xwo bxesehoq kmoinuWnuurbMuseQuajdi(), zleb lop nxe btaudn yu i zuhrjo DmoisKaqzuoq (leqyilg vno VbJileQuikcab pqegbay oh bua voox kafn), egf cvogu ccu wijro.

Tid gja yidAZ dimfeuj uy bsu ugv (od jyi davvetwaylepd MemvoxMofidacaWaerTurxbarviw.mlurw), ujo fzo hdonoyoh nliijb ecmir msihinzl. Qojvghohe doibQivom.hzuuxb, ugturi cra dpeupn irdoh iwd sevuaf cze wifpi. Kie xon elluoxurbl uqqune mxu fedkot joqbe dajq ab fau gem cam nxo oOV amw.

Qen xou sjiiqg yu odqu ce ikak gle ehic’d kakd, hapufw e ovah akw meu nxiuf zezcegah xvain codatiro egkuof af jlo adv bohe ta:

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:

© 2020 Razeware LLC

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.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.