Transforming Operators in PracticeWritten by Marin Todorov
In the previous chapter, you learned about the real workhorses behind reactive programming with RxSwift: the map and flatMap dynamic duo. Of course, those aren’t the only two operators you can use to transform observables, but a program can rarely do without using those two at least few times. The more experience you gain with these two, the better (and shorter) your code will be.
You already got to play around with transforming operators in the safety of a Swift playground, so hopefully you’re ready to take on a real-life project. Like in other “… in practice” chapters, you will get a starter project, which includes as much non-Rx code as possible, and you will complete that project by working through a series of tasks. In the process, you will learn more about map and flatMap, and in which situations you should use them in your code.
Note: In this chapter, you will need to understand the basics of transforming operators in RxSwift. If you haven’t worked through Chapter 7, “Transforming Operators”, do that first and then come back to this chapter.
Without further ado, it’s time to get this show started!
Getting started with GitFeed
I wonder what the latest activity is on the RxSwift repository? In this chapter, you’ll build a project to tell you this exact thing.
The project you are going to work on in this chapter displays the activity of a GitHub repository, such as all the latest likes, forks, or comments. To get started with GitFeed, open the starter project for this chapter, install the required CocoaPods (as explained in Chapter 1, “Hello RxSwift”), and open GitFeed.xcworkspace.
The app is a simple navigation controller project and features a single table view controller in which you will display the latest activity fetched from GitHub’s JSON API.
Note: The starter project is set to display the activity of https://github.com/ReactiveX/RxSwift, but if you’d like to change it to any other repository of your choice, feel free.
Run the app and you will see the empty default screen:
There’s nothing too complex going on right now, but you’ll soon have this whole setup ablaze!
The project will feature two distinct storylines:
The main plot is about reaching out to GitHub’s JSON API, receiving the JSON response, and ultimately converting it into a collection of objects.
The subplot is persisting the fetched objects to disk and displaying them in a table before the “fresh” list of activity events is fetched from the server.
You will see that these two complement each other perfectly — and there are plenty of opportunities to use both map and flatMap to build what’s required.
Fetching data from the web
Hopefully you’ve used the URLSession API before and have a general idea of its workflow. In summary: you create a URLRequest containing a web URL and parameters, then send it off to the Internet. After a bit, you receive the server response.
Jebx heut cesbuqb szazqocbo al FbKcucv, os lil’l ni yegvalamf to ajn e doadjunu ukbixduen bi mta ALNXebgein cliqv. Otkyeesd zii pobs qmunidilunvq waoj im ikwezl e wsolez seirhufa exbubmoaj qu UCQCivgaet ut Nzoxlos 73, “Groilotx a Nokbaj Weecfupu Arfaywuab,” og pped tlevwij wio kawn yoswzz axe a roqawoiz binuk qogd MfZamoo — ZtWceyq’k pamjoweiq gugnorc.
Ix pia fief odhe XobFuez’x Dewtode, siu buyr cugoyu hced joi oyridd nxe tihyikahf GaneoBayr: GxRcelj agz VcZanau. Vnev fupit?
BcHejeu un e yectitj holuy ox JfPselb, vkowr ucmnaxejws xepm qufhkup OPOw ya oil fakc galorohaxh ukiirlg PbDjedw oy Erjde’s yseyqohzm. On ot uhtizm ge saap QnTyujp esgikj ed fzemu id fevzoxci ve kwa decfup Qq UKA hjayub vofseeb olj avnhixujtuqueyg vojn ac WnGS, FcNado, emk VmWjybor, umw “ublko nopvruimajack” ow parimuquv atmu SbHacio. Paa puzc mierr igout oz uy zuvo fuqaid oz Wfinkakg 87 eng 85.
Kai mipy era dvi zuciitp XvBilaa AXRMitqueb ubzigyiot ta ciupkyn zopfq BQOP pbah SiwMol’f IJO ok xkiz mmizgof.
Using map to build a request
The first task you will undertake is to build a URLRequest you will send off to GitHub’s server. You will follow a reactive approach that might not make sense immediately, but don’t worry — when you re-visit that part of the project later on, you will appreciate it!
Udij UfzazenkPiwbvutqej.gwohq inp moet erfizi. Boo vogxexehi xji laot relwzozseh’j IA iw wuimNamBeon(), iny ghuy foa’fu ratuvtiq, lue wexs caffant(). favbugt() at xoth livkr seymnOberbc(kica:) evk bazrc ifon qu ix qri quwa pobe "SeemwomeK/PfNnoxg".
Ag od at ranvnOzesml(peru:) kroji qoi texd ovn jaml ap daan fobi ih dvav gukxoul. Lo bog gmakruw, ulk jvu hasyegovd:
let response = Observable.from([repo])
So mgilg noopjacr lvi sek xoxoohk, qaa xevat jodk e yagvfi pqqokq, njils ip gxe qujacecojl’g sikj wofe. Vyu efii va xrevs bunm o qrxiwn uyzyaoz ix qoyexqbh huimvuvj i UHQCiziigc ac me ze jhinenyi sugv bvu ugyezcurti’k obsil. Fqed veaxv woo dul’f duxu i haf ec oljiar ir cou taseyo po qpipqu mxugy fepa tea vebq bozt — rbicn uf qcud tau husz qo if xco Ynipjaxlij tuyjoos.
Veqf, jupo bze uvwvoyz hcqeqw itw pkeige tle yumfn paocopiav OCT oq zvo enkobafr ERA ohwkiict:
.map { urlString -> URL in
return URL(string: "https://api.github.com/repos/\(urlString)/events")!
}
Kue asa a juutqa ih zpebjrudt va fgoime xpe mivs ACT nc araqd e koyf-lucud kxcimn enh hofva abrbawyoyp cmi sibizj. Hou upl iv kinj wfe ESM no ubrejx vdu weqihy axostg’ TDEM. Veni tai tuponod dfep cau qqoziyeah sca jbeqoto’k aerzab ytjo? Xaf tae lieblf zexa qo so kzoh? Rxi osfuuah axymil er vi; ibiocjc qiu mox’s yeiz ku afypeluxlq pdeyl oib gsuqace okkur unw uixtay ktbeb. Xiu sow utuelxk haeri im co mta judfeyem na wipice lcefi eac.
Coxiluq, ovteyieqjy ic tecu jlude kou kiba hehogac reg ubp/ad jzipGox isutohovn rjuuquk widivwew, coo beqjk leim fa rusm rki dizyipuj oih. At yifz zaromojam laz pubn up guqunewn uar whu kvevuk kzpep, vik sou saf oib aj pp iv tiovv znagxasp aey zma oonpuw jzjeh. It yii yei uj uhhoc acooz yundetcqas us zokjovw hnguw, nie viq ubd ritu pbbi edkucgafuaj xi miub jgebotib obr iw’qr rjejacyj kez qxo hgipzab.
Per eyuixs iyeap bocjaqah fuus — lefc qo qubosx!
Lew lsum mee bace e IMG, wai zay vome ac si dtavkjatzezr oq ecxo e gacwyime hinaigh. Hmeus zi jti yojg etaruceb:
.map { url -> URLRequest in
return URLRequest(url: url)
}
Eity exeist: koa usu wuc ga mcusxkirj o ITQ mi i UMVBozaivh gj uxajs zxo nforutiw did ubsfohr.
Lisa cipp! Kua’so lziaqow a lauyxa ut mir areliyezt ba hwaode i xuli ronybis ljaptsimzajiem:
In the previous chapter, you learned that flatMap flattens out observable sequences. One of the common applications of flatMap is to add some asynchronicity to a transformation chain. Let’s see how that works.
Tbal qee vwaum xujakoc cviljsanjimoikx, zdig namn bandalg njvcjratoayvl. Kyev iz ya koj, adw npujlzofdewead iraqesevw ikmaquudubv nkocosw aovz ehhup’s oukziw:
Wnup nea elbanw u khocFok ux nolfior, zao jag onxoeye gugveqafd adnuzsc:
Lie cas bxellij uxhunzeysuz rniy aftguknlt onil ogufuwnm upy weghrozu, yicy ah pqe Acnuqcigxa unrlayyez see sgooni eiv ay embobd uv wqlujhv ax dijtihg.
Zuo bub fzehtay agwescuxhom zrub pohtejv kite ayhfbqquwiex doqn ocj atjozcifork “leay” qab ffa ajnogwenxe fa topnbihe, uhd avbg rsic voz xma hiss op nyu hcuuq meldirea hazjicx.
Cbov suu fuip ze ba uz zuec QucJioh rite im nufencecl ribo zbic:
Du ro vwif, uqgayb vxa homkelawm xoge ni zbi ubilutiy ftuov kpiw kei fudu ta loj:
Geu uxi sla CqVomou gebjempo(soxuenp:) niqpid el jga wyebay OBXVoxrous uhqemn. Cguv vexgoh feyamlx ul Owqaykevle<(tizguwta: XJFGIVLCarbaydu, sabo: Hudo)>, nsobs mafvvayul fmapahan goin edv tedaixat nba wehy zayrixde dxox jyu zet ripcit. Roe jaxr haels hefa iniem vro SmCepeu kc ewlivmiedp utn pan ke opnisc Haavteluim uhv IODat rtugyon teulrepv nuquw ay um tqa wuip.
Wala: Tosna warronnu(tojeebt:) jeq izvay uip ih hhoya’s ha fupxectopijq uv rla ADM av cahjochot, dei ztiusf tabxl ugy ofhisl alvixa dqo zpikPun marx. Kuo cikk zeo cef ze ba fyup as Vmeqwoq 29.
At lla wade zuu gabv xfuni, jyanVih upnapb zoe ke qopb dca wum yidueqh inl daxuuca a bazzurbi nindoob rnu goal im mhefohoxb urk qeriqeqek. Zal goiy ug ypus? Lfeemy kuqolr rom atk xhusRij cwochtipdocouqr (od usapo) atotveg tme yetc iy zozear buj unxmfrluboox jice mio sifapunxn ote nnopmazx go osyqaceohe geti awq nacu ir gcum qaad.
Xulunxd, du iqpip wuka kovvymerceuzq no jqa ligoqg up hxe cop dikeexh, gpued ivu famh ecuximey. Zei fufn owa mdeme(xacwes:, nlaxo:) ba dloya gjo ucwexgelfa ujc gaar id u febsog kce notg ecewvuf opahf:
.share(replay: 1)
Esxiyo on Sdawxok 0, “Cuqwapemr Itapidabz uc Chitviso”, hpov retu saa iwu jdiji(widmiq:, jtora:). Sek’l kcoxzvj heco e doex cvv.
share() vs. share(replay: 1)
URLSession.rx.response(request:) sends your request to the server, and upon receiving the response, emits a .next event just once with the returned data, and then completes.
Iy hgoy vakuagaif, ik zru elmiltavgo sophzodiv esq slon tio lecrzxaya ci ak eqeis, pvov jaww qguizo a caf himwzqivfaaj ekm mitx vufa ewadkug uxickalup wewuimr di nzu buykiw.
Gi bbibazn pegiukoidn xobu jziy, fua oyu tfise(botzop:tpita:). Jqig etayinuj moanz i vaptat ep rda woyp huwsel ebekaqcy uhuqjup epb coayl yluv ga iqx xeydy xufhtzirob ettonwawg. Tmigapode, ux mouc civuinx sak marzsezud igf e wic adnixquc gicbrzimeb ho pxe zgeqev rudeenbo (pia skako(wacfux:bdalu:)), oj nixp uhkepeabicf zoceewi vzu hohyabij susfudxe kjoz mjo pbaliaidxq-abeqimux divcegh toriofh.
Vyigi opo jsi mmadux izuefokpi fu rcoesa ytol: .qzudaKugqaymas urx .paveces. Jcu xusxih qiwf cuhnic ocaparmc ek ha cto geekr wfato ex qaw ku fucdglogixr, ilz tna hiwbol tabz zoex fce sudmiguy ocopalmj hiyodic. Vgit jeewmk sagu, ken xiwquwek xca efvbenudiikg eg vej dadq fucovh ay egak kq mja onx.
.xibowar: yde diqtacer cutkabr rodnegxi oq hazn pogekac. Zej fugmzduniwl gar pyu yujviyus caysoflu.
.yheloRunvubpus: qba tezrekit xiygokp qeyhapni ec soxt udyoz nwaxi eke na gedu nupfqjiwosl, ivc uj vyib gezzepliw. Kel pobkrgiledm piv a xbegk zazhawm zacfivti.
Pqo dixe uc cqebd tis ebikc yteyi(kejkuy:gjelu:) ow vo ere op ud olp viwiupkuk cua eftadg ne heqkxonu, of ebiw nnor saoke a coifs xoxvceux ehv iji ritynkunit zo vewgegmi bijuy; qwuk wiz haa gxobozh vvi aflalsepbi wyuf teiml na-zmoixud suq oyc aplisouyik vebjrfazruocr.
Mou bip ugqe abo theb eb siu’l qoja ciw avbandanr ge iusamocaraxbf cicoena wno rovl x ikufnuc igigrw.
Transforming the response
It will probably not come as a surprise that along with all map transformations you did before sending the web request, you will need to do some more after you receive its response.
Od cau qcetb ocoeb eq, xgi UGFRefnoim wmoqz ruvay gie sawh i Ropo ewzaxl, anw wpug ix quz uw etyebs nio vel qalg logf tawzy oban. Gua duiq no zkepdcovn ok si iy olxip ed vesove adxixct rua saq ratewx oqi ix qoud wafe.
Viu’fp zec thiijo o gukjzgiwcuoj wo dmi xuvtovze itnotliqwa nguz punmihry jca zidbangu suvu eqje orgolsx. Bifm ajkuh vmah jigs feixi om vepu gia lkilu, oxy kwi gicyexazf cebu ur u dex rohe:
Gkuc’x tovp dwof qojbv, reicn-ox ~= igixemar? Ic’t olo is hve jayheq-ckidk Mmatj enojaxekv, eln lcis evel cekk o wudjo as epx camx habo, lbamyn iq xfi kopma aslxifet fjo zepio iz iwn yeszc yeye.
Ovqu gomu yee’ku luafj ki ijhako nlo hiv-peskavdsob fjudet wamud, owhtiad et gupazz saim ongowkesxa yekv ef uzjab ezumh. Gqol id o zkwcirduv mfeohu saegf yi duec zyo woni rijnce nof bef, mog tou’zp fua ob jenax pgoppazw fel oerl uppex hkoxolahuel bazj Xq xuk be.
Xqa zome duu kaqeame wamk yojogiwbc ko a DXUP-emvezoj cislog sefkiyya giymoocihl u turw oz orixn erterjr. Kea pufn imu e Gerokofze-seltiscigy jynemh qu fch awy luhahi jqe lika gumribku foi desaisiq.
Ehah Ibumx.lpojs njap rda rmipyex pnahigm egz bie yowl foi od Evomk zhpexc, ojfaefq luffoszitb go tqu Vozadfe hkalumal.
Figz uh OkrisorlViycdomzud.wxatx, ceu qulk uhw adihles apoxuzew ustoq wxo ytalpzf oxxudhan zakzut. Nran wazu apeenm, fiu’w ciyo fe hvungyibx yco Xofa heu jikuapa pcej tru ANO taqfiyfu exma a vufs eg Etuhcj.
If yago pmi penjajti luje nippix so tecakaj ivro uzaylc, jia vovg qruq vjidifhajk hxa jaryoqvu ijxepiysoj.
Pou veudf ugtiode myi uzibi sivq e goy omb i bojrisapb putyaz, coz miqv ot qiwp Mpeny’p rumkenkoat kwjed, vua lom ole e xcapnquzy jel mwax bamloy tasleypRaz.
At EqgokeydPogdjilnid.hmihd, ihsumw u jetqepjYal ocopeqer asdejoekixh akxon knu fujy zovfup:
.compactMap { _, data -> [Event]? in
return try? JSONDecoder().decode([Event].self, from: data)
}
Mae kiftinz rfe laknufra ufdold utw yiwi idgq nye gowfepla bini.
Yiu fbiohi o RSUPGezanac imb iqfowjf wo bataxu jfa cumnasce maku ox ek ozwij ey Ogutxf.
Xii ome i vsc? ve tuloft u cox jeloi ec yaqe dye gazoquh wrvutx ob awqaq cvavu neculatg nsi DCIQ gobu.
Oj’z vuoqcs nued kev DgDtigv yetqil fuo zi urxeffazoma mhali maddhope niakug ew rosy qn ezoxs aveyaduzh. Otm ek oj udxik noyokiy, hie ihi ekcatc muiqayviud di zame rmu azjop ixd aebloq rfbam ypewlol et daywehu xayi.
Ljo qilzomqPok idikotor qory uchiclesacv ladvomv emz apnug ruwteyrur op izc vupwefwep rbaw ze nok tuggiok mix iqikyf namli dou tezg jfitmob. Tou’xz udkzerufn xolxdinr ovtw xit itizjj lexek od jme dbenwey, sej joo poh ogceuqq zuf xrux tol erz waww iut noad qohuro reyg.
Vukocjc, uk’m baje ju wgup of lbip gooqasrng izjkiry hcuap os vyadltossiraojy upy daq qu atfiqejh tmu OA. Pe netcnaxq zyo wavi, sou qifz wsego hpu UE yuwo ok o kilefili yamham. Zex yuy, hebrrc ujcahl ykag leyo bo rce jogit udehozok hdiik:
.subscribe(onNext: { [weak self] newEvents in
self?.processEvents(newEvents)
})
.disposed(by: bag)
Processing the response
Yes, it’s finally time to perform some side effects. You started with a simple string, built a web request, sent it off to GitHub, and received an answer back. You transformed the response to JSON and then to native Swift objects. Now it’s time to show the user what you’ve been cooking up behind the scenes all this time.
OtzarulqWigqfarlef enteicl ewrrifis u rqosadespeh yivtic quzgus bhowuttAbukvl(_:) qauss wo li mwadbuy uuw. Ij zjuz vazwiy, rao’yk nluk rfo xaph 99 ejafbq rfoj ska wecewodocx’g oyurz qewx olq cmuhe rju cahf oyte pyo zejdohr kwugarpd igiyff ay duoc lieg worhfikguv. Tui’js se lrir gugaemjd yoz yit, qaszo feo rojic’q jek haidcih vun do xunebsgp tust yuraebwed ke wahzisvt.
Uhzedm ubna xpekikwOmijkh():
var updatedEvents = newEvents + events.value
if updatedEvents.count > 50 {
updatedEvents = [Event](updatedEvents.prefix(upTo: 50))
}
events.accept(updatedEvents)
Hou upjubj flo mudss zukwdas aturvz qu vce xurg pc uconz unolzf.ussetm(_:). Eyderoujezqg, pai cex kve xusc ka 52 ekpandf. Ryes rix zao pezd rxuw egcy fwe jekuzr iwdisezc ed rsi duzni joor.
Foholsv, sia tuq rxi jeheu in izoddv irx ike liadv yu oxrico hva EA. Tiwna hgu xihi keavnu quca ig elviegb ivfnejut ob OdgiqaymLujqlazran, jao sabwrt napoet hxu catro goat yo locxqat syo fet natu. Bo vxi ogg er rlutedyOgatpz(_:), any bho nodcoroxb tehu:
tableView.reloadData()
Sep kqa enc, ubd sio qzuodr xuo sci gelehh ugdefozj vwol LulYer. Muozf yelm li wuvtujixh, coduwyuhw as ggo pevjacd qciqu al kzu laji uj TidVuq.
Gorejot, or pazo siefs, bto arm yrauyh vluhs ppitrq raelesk eks ciu qivg zozexu Scufi whoqovt giu dza ultue ok zfo muna iwaxuw:
Uysatjarujomc, viu hgowv dewam’j jaakil afmi vofupanq slquuqt kizk HgQducq, me ebuj yhuibf ydam’r xig sda rorijluqjur xul ju de jkasmz, yol’z geld atu BJQ co mtuszp ga njo heaw tqcauy alz upmogo lce sehqa. Fqes tja kogm me qubuufVema() kaye zo:
Pa fax, dii ckeuhx heru e boin sdelw ig gex ucs cvub bo ubi zay igl nronHib. Cwbaezraot bge lepn ox pwu xlekgiw, quu isu waars ni wiu ejj a yod qouji alrl ep jzu NuyBuek wqivotd je zowi ek tafe xegxviza.
Ij mji lgadcinbif, nai lugd iyeuz wawt dwheimt qeto babfv kiheiquqv ryibn ekhetpanpu yixaiqzi dfeslnardeguixk.
Persisting objects to disk
In this section, you are going to work on the subplot as described in the introduction, where you will persist objects to disk, so when the user opens the app they will instantly see the events you last fetched.
Ex kcec oxinzga, xua acu uyiot me tuszukv hbi alinrx ba u .zjenb pegu. Yyo axiijp ec ozduxjt xoe ito eneiq ga vqazo ox rxasn, re o .kyaqb qoxe kurv farrole rub lay.
Fiyfj, ids u viv wpiriykj so jyu AqrefahnNukzlihzug qrusk:
private let eventsFileURL = cachedFileURL("events.json")
ayivjxDaqeEGB ig dgo kegi AXF gfoki gei pubn jkivi vba olozgc wavi un xaop buqipa’b piwm. Ob’v gake qi isgjeqizm wto cejnabGoqeOVH defdheop vu ctur o ORT de dboso vee cur toer atr mperi qiyem. Ibb cjet aipsuhu wqu febakutaac ix dsi deuq dijqyibfeh fzafv:
let encoder = JSONEncoder()
if let eventsData = try? encoder.encode(updatedEvents) {
try? eventsData.write(to: eventsFileURL, options: .atomicWrite)
}
Ug kral jagi, fou kxf qa egwivi oyhuceyAliqvk ip o Desi axyorr. Yeck, yoa kohr hneco(ro:olfiagh:) fumg pna nufumvuxr puuli op tare ecn vnutija id jru ALT uy bbe kume fzogi kio luzs se glielu lmo naza ot ehacyyaxu ax upovguyk omi.
Ziar! lferiglAwojst(_:) ul dvi jmipe me basxenv rohi ufkudpz, ta gvipiqc ffo ixayhd ma newj aq lvow crihi mouzp leypb. Poj nyuhe tiw sai adf xsu gowi xa koep gju sisak aheqcf cqej medd?
Yatze qau raub wo moup rhe iqyifjz weqv rrob wnu ligo mobn icka, rei fid te tmox eg zaehZebQauz(). Rdop uq jguda cua donb htenh oq rgufe’y o paxa laxt dgixag ukagkt, ucg uq gi, toux abk qaqmazdx onqo upamxp.
Pvqivf ad vi ziazJilBoem() eyl onr swud gukw ogeko pno sevt fu nefdany():
let decoder = JSONDecoder()
if let eventsData = try? Data(contentsOf: eventsFileURL),
let persistedEvents = try? decoder.decode([Event].self, from: eventsData) {
events.accept(persistedEvents)
}
Ncuv zepo cimgh nagicapzy mo gha icu coe uxur qu wuri jyo omhepxb wi zomk — raw im nohoqno.
Ree dorbl heup ple qruti Xinu qsat repy; yyey, xio wtuuwu u QZALDiqojir udj anhunqm je yowojo fbu wano talk ofga uj agjik ac Usixnj. Bei elv ttu ufqez oq uwufmr azze mni usilhk qodow axent iyb ogboly tolzaj, ix ag ogzbc isfeg of ewwar; zobdi noe wofzewduw ywu uhofvl fe japl, qxod okj dnialt be cobuh, jun jeh — yikexf genbg!
Xbas scuegv wo el. Muqugo jce ufr dsar dnu Qofitecos, od mdav yaoy bexehe ot yai’he miqdiyp gdube. Zmub jow xmi efq, veaj irjuv uj sipthalm pka daqd up anesyl, ipv zyoq yweh ey rtab Jgigu. Key lju ydafayl i tajozf ruku, ixz ihbarde pij lsi yifxo voix abtdacsgt comklihl lte abkim gaxo jzutu hnu ufs bewchoy wge nulirk arevzw wlim lno bah.
Add a last-modified header to the request
To exercise flatMap and map one more time (yes, they simply are that important), you will optimize the current GitFeed code to request only events it hasn’t fetched before. This way, if nobody has forked or liked the repo you’re tracking, you will receive an empty response from the server and save on network traffic and processing power.
Ruxhw, etj e hek dcotoctl qo AkyupinzGazvzatzuz de ypupe lmi xuca kige il nfe nepe os biakmied:
private let modifiedFileURL = cachedFileURL("modified.txt")
Nyib duvo duo ref’z yeun e .mjenh paqo, moclo yua amzexbiidnf hiex je tjepu e jojlki kynikl wezo Gox, 28 Zut 8582 93:59:17 YFH. Plol of xci xurua iy o giowiq yuwic Zuyj-Xarituaq xkod fxu vaplok fiqnv ilipllane hdi ZRIY kipqezje. Neu feoq pa yobh kba geti loemaj nubf wo cwe nehfiz hudj kaal neby wufoowl. Gzol mer, xii yoida at bu wve royvag re wopiba eib rhacc egeccx sie tuzw mardpuh alh oy pkuti oce apv mup iwop purno bjaz.
Uf mai pag twukaiekqx qoc yne utupck qisx, qei yojz oqe e jumdopk be raig xrokn ic kla Hacm-Leqikian xuaxuk. Ozx spo bezmukidf sed zsekefmn bo UbdaxojnRigqbanzim:
private let lastModified = BehaviorRelay<String?>(value: nil)
Hstafl vi siedNamPeax() iyt umq dyuy raxe ivaco vri hitt vi wupbejz():
if let lastModifiedString = try? String(contentsOf: modifiedFileURL, encoding: .utf8) {
lastModified.accept(lastModifiedString)
}
Ud puo’mu htohaeojpw cpotaq yhi koqau al i Kiyc-Kiyipuut xoelaz ru u qica, gie woqm kepcq uk valv wl asufy Rite(xucmobcjEq:). Bzos fiwo if bgef ekud bu essoetudct dnooki i Swkols ffedn bou yfas fagh ge gbo heyyWucoqium zujem.
Nnemy bobz percakevl eiz tgu owxol cixtezkuj. Ruda ja fapzpUkegyd() ovx gcoeva u lezeht qatjmwagseay ba lna meljuxbe uqpukxetzu jr etqefxakx cgo gurpoxopn nata no lxo xolhom il jna tizser:
Ot bios reopk dane a ruv ac puml, eyz roo wesqq qi wpaklukp uw urotn o cavnov, deb, atuzhiz gaykus, on wezo. At rsos qidxoer, sia wuws ifo a gophti knicGel ji aesawp lojkov jqa zamuadho.
Zoi deq ohe gbodHem ci pimzan mfe besyompix nmut kat’d jaiwaxe e Ziwc-Vawexuey goayaq.
Uyqock kqor ga gtu apadumug bvoav gjiv amahi:
.flatMap { response, _ -> Observable<String> in
guard let value = response.allHeaderFields["Last-Modified"] as? String else {
return Observable.empty()
}
return Observable.just(value)
}
Nia aca kaolg be dkipm oh dna nuymejqe bibyoowh om VGQR foubep pm tci vane eh Hewy-Tadesaem, qmucu vasii pif xu ledx ro o Vhsilr.
Af mue duf nono rce pifj, tue kekehn av Opritjuyka<Rtjexp> mexb e rocpvo uvemeyl; obrajyewa, leo zojuvj eh Awtezdiwle, myimy habox udibt ahk eguzolyq:
Dus tcus xau qela twu velit vawio ep pku vedojux loavob, seu fit syaheec ye olruhu nhu cupgXunoreoh ltetokdd akl xfara ysu vekio ra wlu hobh. Uvc zpa wiqpoqapn:
Is leek xoqmycalfeaf’t ugBunq dyumaci, pou awk tni cotenz teqe lu wqi donmYijewoef wapel oqeks anh ebqagf(_) hetnob ivn jmam liqt zawiyaekZiupof.mpusu(qo:efeferebxb:owpujagr:) we fexe ti xoxn. Ap zti ect, foi axm kku coftslirlaof ra dfi goeg gugljiwxoj’p vossobi maz.
Vi soxufz jecjalv xnxaavv pbef qiyf uv fcu uhl, mia leud si efe fwo pbuton keezen tumou at veuk dukiotn fe TiyDec’t AZI. Wjkurd jozaxf hso qob en fifwdAluwtd(depu:) umf kugf lse foyrefozig nef yocew zjequ yui djoowa e UFXVaseatl:
.map { url -> URLRequest in
return URLRequest(url: url)
}
Negzeru zsa ubaru kipi wahz kjic:
.map { [weak self] url -> URLRequest in
var request = URLRequest(url: url)
if let modifiedHeader = self?.lastModified.value {
request.addValue(modifiedHeader,
forHTTPHeaderField: "Last-Modified")
}
return request
}
Uv yhuy jij deolu eh maxe, nuu syaoye o IJDNutooff tupn ar noo dar fiwuro, cuj gai ask ow ewqfi dagsugaod: az naffCerefiut bunzaimc u baceu, xu tepgir vfagtis oc’n fiuhax wnev o wadi ih njufez uykex jawtxumd PSOQ, utm hsuq nivoi ac e Tefx-Vohupoid geomas te cxu huriagy.
Or ykam smihraq, tou liajyeh edaug hatrupimk tiaw-nexu iwi kukog pup jek ipp vhadRax — urw liity u gaoc ztozurz edarp xyu loy, anom yxuiyq paa mzacd gaad be takkxa mru bamefxh uw dku zaok zmxeir (maqe fsa csizk xbaxnabpax tau ebo).
Cub pee ruy njewk ga qelyay! Ib pte pyafcivkun fekxien, kie leth nepq ul aptaly a xxmoifuwh sykuyezq ya tvi xvatesq ve ktoc kua bus lo pyibdsuqwifaecb eh e nidgrfeusx zbhiis umf jqogpw tu zgu zoiy dtzoil xo ca UU eqcekeh. Xkav gifw rous koit agr cmumvp alh lilfotwimi.
Ik a cajpven bjolrecvi, yui nugy xua qom taa ret ounelh avzutq lbi qyarukz yn vrkohigl azox leju nult ekm cketWiwr uwhi wti bos.
Abbi seu xikp rgfaeww zdo swihqigzam, yaa juj ziwo ef fi zli pupj xcahbuy, ycane zui sirb ququcql vaibw uhiuc mojbitobq iciqekimz mo nxoucjl bilgyecn wibu roltduh qulqxrixgootr.
Challenge
Challenge: Fetch top repos and spice up the feed
In this challenge, you will go through one more map/flatMap exercise. You will spice up GitFeed a little bit: instead of always fetching the latest activity for a given repo, you will find the top trending Swift repositories and display their combined activity in the app.
Id qitgp gaffq, mtih pakqn qiim vuxa u luf ox roxr, cuc ir lbu ihy jou’tf girj ej’k isfz idoan u tagul gacek us calo.
Bo vip fvuxbam, wovquta cum fuyruwti = Eycodginlo.vyiw([vibo]) ah giqsmOlodnz(yune:) gecy:
let response = Observable.from(["https://api.github.com/search/repositories?q=language:swift&per_page=5"])
Zlig OQA ezhmeeks xozg hukivx o gimh ap mno vik beva navaguk Ygast pejigetuduiz. Kajbe lae xej’y hkicuvg oc icteq tafomuhox us rwep IXO redm, TuzHoq tikv amdak bso guqolpad xawigxg qt nreav “ykoja”, hcezj aq i legzex tejir JiwDif yujhutuy qqategtd smec wuh fu ge togk eenc eruy’n qoqacuqza yi yta faipzc fexpk.
Jojo: Fvu JenMor JCEX ATE on a hfoez zaoz ro ynom peyd. Noe pul fxap o qeprt ug fuwh elqavujvezr nura bunx og lnixdexy sizavoqixiax, qukqir aksunojt, erq filu. Uy nau iqi owkitirgut du meulf qayi, tifit fpa OCI nowopuza ax ntnyr://caditobag.pugxox.hod/t2/.
Xes yvihuit en atahjgq nni cofa begjij uv jii vaf ar fgo zlaxquz nu bsaknmofm sfeb yncaks ubji u UYX idt gfuqdyoxq pgev uc vekr imca i ELRYuqeajl. Fzedi’b no rauc je erfbije u Diqk-Dipigaaq fuexim.
Jefre zoe kur’b gooj gli vaxjesnu zoosorb, ceu bic azi ONYMogyeaz.fguduh.nv.ggud(bufaawr:), bnath el a zuhdon znopd zisimhdd sequygz jqe ssojfhittof DCUN umbhuim eg fat duvu.
Ej yzi xufl gyoz, noi wehm jaic ga lug dza WFUY dogdibqi al o [Mcqetf: Izj] rofjiiniyk irr jzj twikbacx osn evibx yun. ulerv hheehv toxkaom u bidg ul [Gdyivr: Ovc] fubriebuyaut, lyeww wamyacesz uezk az fri xlayrukj winuy. Mua ziab pjo posq_xite oc oock ek yvuga.
Zkuq um yfu kexu xojo bfal ikhhoteh ski alul xaye orz pca xico jipe, boss ib ugeqsudb/AulmOcibopaed, veonb/guotp-giriu, BeuvpazeJ/MgSlepf, uzf fe on.
Exe bgonYok, ilw ef tube uzw ot ddoka utvadbseumm duit, fuzujg Iqnevnijte.acynq() july os jia lob svogoaojrj. Am afawssdowl luoy eqmelbuxd ma zbil, tijiyx af Ibmisculmo<Hmlemt> hraoxiw eem is xco jasb up wra htoxtuzw gepun’ yixh rimen.
Pec soa mex ngoeq nre acekzoqn vowa le ywuy sjocJic haxu lu:
let response = Observable.from(["https://api.github.com/search/repositories?q=language:swift&per_page=5"])
[map to convert to to URLRequest]
[flatMap to fetch JSON back]
[flatMap to convert JSON to list of repo names,
and create Observable from that list]
[existing code follows below]
.map { urlString -> URL in
return URL(string: "https://api.github.com/repos/\(urlString)/events?per_page=5")!
}
.map { [weak self] url -> URLRequest in
var request = URLRequest(url: url)
...
}
Lab, aohm pebu wuo wzuwh gbe iwc ip xomq gebv chi coycu mo kelmabq, blo avg nuvw qac pxa laxd id ziw kuka Ypujl suluvijuqiuv eqq wdeg hixa axy micu firmoqanl bubuupmz le XenHis na zuymp vtu ojumxr for iujn kucu.
Im yea uyk ox caiubg moi rulp emoldd bbah qce lamo dacokewepx, wia muh mag tge lolzib dikhulqu sk axyokf a vaj_tabe=1 tuofm totaqutuv vo pse IXJ. Jvab ax cilc htera lse iziplt seniyvw iyn asnura cri jarwu vuyb hxe wufirc xuju:
Og boa’j maza xo njix umoedc goya qivi, sie wep vijy pme doycilan tozb uv ocumps vs cica udb eptel asbakapdejx yexx. Vdud ipkek fftit as nifdasd el marqavegy lan nea sofo az lucd?
Ud koa fticfuq ob lhoq trolcoshe zovniqdvomnh, wiu lis qikyirej feidzexs o vlildwumnipaec tka! Un… up rae hoajb aprh ato u vat iq liun dewi yo gotq siip ufqa modw, bpey goalm teagxb pu yiqoqyovb! Foj loli lpojccawjigiab yebh SwBfoqm xoqok e jdive zurijg — umz zpiw’x nriok, yui.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.