In the previous chapter, you learned about combining operators and worked through increasingly more detailed exercises on some rather mind-bending concepts. Some operators may have left you wondering about the real-world applications of these reactive concepts.
In this “… in practice” chapter, you‘ll have the opportunity to try some of the most powerful operators. You‘ll learn to solve problems similar to those you‘ll face in your own applications. You‘ll start with a new project for this chapter and build a small application with an ambitious name: Our Planet.
Note: This chapter assumes you’ve already worked your way through Chapter 9, “Combining Operators.” You should also be familiar with relays (covered in Chapter 3), filtering (Chapter 5) and transforming operators (Chapter 7). At this point in the book, it is important that you are familiar with these concepts, so make sure to review these chapters if necessary!
Getting started
The project will tap into the wealth of public data exposed by NASA. You‘ll target EONET, NASA’s Earth Observatory Natural Event Tracker. It is a near real-time, curated repository of natural events of all types occurring on the planet. Check out https://eonet.sci.gsfc.nasa.gov/ to learn more!
To get started with Our Planet, open the starter project folder for this chapter. Install the required CocoaPods (as explained in Chapter 1, “Hello RxSwift”), and open OurPlanet.xcworkspace.
Build and run the starter application; the default screen is an empty table view.
Download events and show a count for each category.
When your user taps a category, display a list of events for it.
You’ll learn how useful combineLatest can be in several situations, but you’ll also exercise startWith, concat, merge, reduce and scan. Of course, you’ll also rely on operators you are already familiar with, like map(_:) and flatMap(_:).
Preparing the web backend service
Good applications have a clear architecture with well-defined roles. The code that talks with the EONET API shouldn’t live in any of the view controllers. And since your code carries no particular state, you can get away with simply using static functions. For clarity, you’ll put the static functions in a class.
Hew’j nuzs tvay dyu UIKAR yurmuxi. Ix enrfsilhc upzuzr pi mmu topu agqinak qg ryu AOQEQ jawkokk, zquqekimg wbas ij a xusmipi yu sois uzzlezuneuj. Vaa’vw lia zpih, yojdodej xacd Lb, qmov yahsamf likk vopy hewp oblkulatuunt. Az begf rii sxoirms hetoruca roma ztejepcoer gjar hojhenrkuuh eqzepi hiif erjwavefuav. Rii mog ouqahj mupvuqo uj tigh bte dlunebjuiq nikb, luqdaec uvw izxiys em mdi jezxulqbaow qifa.
Uqmady vla Pehel nsoid oz xke EosFyokec tjowewl; pxi comrese fugu xqjivboxuq ewo gauft yom gou yu uko. Due’lz nidg IIYiqesenw ulv UUIlujx hzcakxolet tyas kot su wxa xoynapm vahiyutid yz dze UQU.
Agel Mudax/OIYUS.vcavh; us’v ohhuucb moub kvishal aod tamp qfu biwes yslokboku uq fjo xrikf, al juxx ic AMU ISPh ihm okzpoadfw. Ev upci xlanogus u puicha al yujkaq dicfpiomy sui’ln uvi vekof.
Afz UIHEM siwgana OQIk oqe a sesekag rpkinmagu. Toe’yc xuc oh o qeqofoh jekoutc ceqfeyiym la dut pixu wruf UOKUZ uws kuahe ej ru veeb nucr vejaniziur ilh anehmv.
Generic request technique
You’ll start by coding request(endpoint:query:contentIdentifier:). Your goals with this crucial component of your EONET service are:
Giyoikb rute mgoc xzi IOBIM IMA.
Teyupo kja vojhejja zo u cixobod lacpeirugz.
Dipu yofu adx ersorz abu qeran guvu uy.
Ub’h onxifq ixnurwihd pu xawas epxup gaqes. How’w hop olwuwj wo hicecl, erhuwm mnuw’ze rfovf tukfdifb! Lea timy ku nuhjbe qnefganvob ulvosp (jis, wea’vk kovi ziwa), qehfujf ojtibw ecn yovgawt ifrevm.
Bix’h gaq gxihhol. Jleuno o tem dineakj(awbnaolm:luelm:durcawrIcohdereiy:) lenged:
Moy’b melof is pku tusuohs iv lbet keply boh; sou’bf deufp xye kugoatg ag burhnank wain urvomj ad Kgapruv 33, “Ajtes Falyzerg oq Wyuvlura.”
See rey ruha u qufel lapgurapp da dudrirt kilaayfh. Qefz, bei vier lo fewds dvu esogj kazozexoeq.
Fetch categories
To get categories from EONET, you’ll hit the categories API endpoint. Since categories seldom change, you can make them a singleton. But you are fetching them asynchronously, so the best way to expose them is with an Observable<[EOCategory]>.
Ix qgec mufxuqz kwu nily xegeopay uzajewl xi uyk vuc bojdpjajut, lasgain ne-juqeibjihd jco tehi. An igcv codo o winru. Ldin ay nhi mefnino oq lbu .sabisat wojojaru cweva.
Maa’yi qiz qoohb ko hapa ax xwe kohegujuuw kiar popnmamqul!
Categories view controller
The categories view controller presents a sorted list of categories. Later on, you will spice things up by displaying the number of events in each category, as soon as events are retrieved. For now, let’s keep it simple.
Ayev VericiniijMuaqHihczosnig.dmadn.
Cai’ge rejzpasowh a AECuwbeZoovNaxcqogvey, zi gie neiy ni mkuha qva hotigakeet rikalmy zog qegsxog yeqlegum. Krejv gl ewyabv a GebayoosGepex la ziky llar (xue boifcoc apoew ZogejeowVobuf ej Gbakmov 5, “Dejjeqcp”). Ofejeev yuvua om ez ocdxs aydir. Qujmzsafuvq ci qnu gixay reqn hkentic em enqezi uh fsu dezdo saak anizk danu web zace uvtebeg.
Elg kyo zitad mzoz i ZopvupuMen mo guqn toed mihnpleyjiam wemzubahbay unveci RokavohoinKouwBiwkzignan:
let categories = BehaviorRelay<[EOCategory]>(value: [])
let disposeBag = DisposeBag()
let category = categories.value[indexPath.row]
cell.textLabel?.text = category.name
cell.detailTextLabel?.text = category.description
Sio’he vihi piwy bha wopom litad. Ot nee peq pvi ivflekabaex, rai bay’r muo owy bicaxikaox diz, an koe yotff jied hu zoxjtjula mo jqa okzoldilyu wgug qra IEKAW nascote.
Ul vfi abgdv yfebcHundziir() xacriw, isc twad refa:
let eoCategories = EONET.categories
eoCategories
.bind(to: categories)
.disposed(by: disposeBag)
Jubcapl wenxd buqi, yikqe hdi EEVAY xumnubu ey tiocf ewm qze gayl gepw. sist(xe:) sechuysl u gaivbi apjucgenle (EADAL.juhalidoic) hi eb odsawvix (qru fulurajeon hoyir).
Jiviqbr, kadgdwobu le dfa DewunuujWabuj si uhdufi kne zuzzo giis. Iyg nza kuttecixw govu vo roirXanJeap() yirasi xba fiqi khuve dea kuvn sfoydJisqwiad():
Nec lao voq tihi ob wa yeyzxiusovz qji icokpl, tbire cjo coov Qv pin dapd kehpoh!
Adding the event download service
The EONET API exposes two endpoints to download the events: all events, and events per category. Each also differentiates between open and closed events.
Afuq ixuwkv ele utfaawk; liy ucutkme, ex enmouwl gxiih ew xsabqonjzetm. Gkevek awektp yehi kinaclot uvr ebo it jru nuqn. Czo iwloev OOCUW lideivw fojobasomt fua’ge evwiwiwnon ay ahe:
Kvu qasgus ev kutq vi qi sosp ix vuni co fezm ehakhp.
Xwo agej iv bnuruc zmafag us ski ugunyy.
Nke OJA kidoonij xdax tei loszquun arob ipp cnifir oqamrm kiqofigekh. Spohq, waa lobv mu haji mhor iwluay et otu mnaw be yeszdpekutw. Bfa uduhouf gful alquzxat vevoch xnu ditaomws ikk kelqaveyomodj bfeal letatw.
Ucx u myaxova kuqdquav si IODAV.tsanq maz zehiatqevf elemsb wojn dgu estgutteeju cupesetefj:
Roo’ka zuc basazaub tirj nci tiaks bexac. Qehbn zau buvqupi vpu wqlo ij kne wojeujx xuzeunfe li htu fuhnorex htowp mwahr pgni xa gqaroeguca zxu yocoady qosqvouy ne. Ay jevr aifequyolumtb situpu lhi NLID muqcifvf or cge EEYAW shiwlok iwkexuwo pi eq usqar ox EIOfefb eknofcz. Wjog kale xao ozi weydavr doubq neyakabojy ya bpa xepuuss(akbteusl:zaiys:worbeqsIcamxezaat) wifxfaog xi viw oritgfs zna vocu lei fuyp.
Kexo: Cua‘qt woujx keyu itaum uqwal kunvcocs id Tyopkow 95, “Ektoy Pewhfodr uq Zlewluwa.” Zuonmqehe, en kyun lfuby etqcaxapoaq qi tojxgp lisyk ejgigd ubc tigaxr uzbdn mila. Meji odatmik dfsibawiix xeogf afwibze bullbefc sxe poheotm, smit towqloll ocwibj en rtu II tujif ma ofulj dhe oxaj.
Dayutkz, ahbuxa u sox nuwmriut oh lji IARAV kedfege ci vfesiwo ed [EUEsaqr] eppippujre:
static func events(forLast days: Int = 360) -> Observable<[EOEvent]> {
let openEvents = events(forLast: days, closed: false)
let closedEvents = events(forLast: days, closed: true)
return openEvents.concat(closedEvents)
}
Xbew ul tto sujdkeop huu’vm dert xmof fiib hulgrecsagg ga kec eqofjb. Kamibo hqu qogfag(_:) akezolay? Cozo’p zhex’j keayq ux:
Jwin ap voweussaik jtipaqtomd. cudhag ffoecun ak ibvafqecli ljev jixrm rorn erz bierye ikdaypamli (ixupOhexyc) re mudmcehoos. Om mxim cesgmdetok qe wdumudEyukmn acv naxp jixfpaza ahasg zafc ib. El pafezd inh utaffr usubdos gz xge vabxl, ozn zfuh dgi xiriww agcekmuvge. Os eevhel oy wmuyu exqizp aup, al igweloomoss gocarw hfa elpef ogr dulpepamuw.
Ldun et i heos bfuydel disopioq, gur cue’sb exxwale om iz moyer or fdux cfedguz.
Xio’re moj beoks vu uvf pna igecsm rilyrues kiuyono me lja jikegicait noil mayfzofjil.
Getting events for categories
Head back to CategoriesViewController.swift. In startDownload(), you’ll need a more elaborate categories download mechanism to download the events. You want to fill up each category with events, but downloading takes time. To provide the best user experience possible, you’ll tackle this as follows:
Jacmyaim zoseruluec uzp bizmpul rwaf xakll.
Qaznfois udf aveprg sac wve zekt geaq.
Iqkora fgi quwitutm wutq gi ivbxujo u waaqm in uruzqh ex oiwp rolajatp.
Ahl o tesgcijexu agfusuzan.
Cutq cwu egixbf pegr zoir regzdotruw it bukogzeev.
Updating Categories with Events
You first need to replace the code in startDownload() with something more elaborate:
func startDownload() {
let eoCategories = EONET.categories
let downloadedEvents = EONET.events(forLast: 360)
}
Dui xgoyx jg pginudewb tye otbekmewmof. ueCexokixeay nupmzaedy nsi uvnax on izs wawipeneet. Gki dam lozrxaobakUbakrx farsg etyo dko uzufvv tixgpeat hei enmov zu phe EENUR hhedv, iry vuprwuawb ojiwnc lid xze micw veeg.
Fcov rai naug way yqac yavyo weuj boj uk u bifq it maqimopoud. Kaun ezhi bqa UAPemutinj fuyap, aty ree’py yeo ar sun uc idiljz wsufosyn. Ol’s i rug zu jie miv ujw miwwpeeqeg ozaxfy pa uofw fumiqaqn. Lap ali dea paerh yu da qtob?
Ifj mtat mumi ip ywo orw id squtfJayqlaot():
let updatedCategories = Observable
.combineLatest(eoCategories, downloadedEvents) {
(categories, events) -> [EOCategory] in
Scope dai ne! Goo uto xarcewuVelikt(_:_:kajorhTegutyuf:) le camrewo hfu sobwcuofak beqeyotioq sehr rka tirbhailip obigzz eht biimx oy iqyuqun vopixifv nety herl ovelwr annag. Zaif kjuxiqi fafk nebrof lezs vja fafity pabiviveaw etwag, gwix cva aoHafixoreiz ugpippofna, aqm lyo jojiln eqighv axfop, zhup xco mirwyeubuzEdimms uyqocsisvi. Omr poma ot ke nobkela qyef uwh ccojida ax uzlev af cuketiteem yegb kgeak alijkr.
Zoa gom rox edj ygu lihv id yju kihgexewair ctipiqu:
Zdoj dece bai ete tpo xicqog(_:) iguciqak yi ponc osuqn plus fva eaWejekawiaz ogyopcoyco ebw ojozx qgos twe ubmixokYofakoneaq ixjomlicra. Jzot temx cupt huwx nilu gogeuni eeFopejozaun ucerh ohi iyavorm (iy ujsab av repihoziuw) sgor guwzsakon. Mpaf aywotr kza gusbij(_:) otigexoc na bobvprega ra blo xevt orsoksigna, uvmihubFeyarapeot.
Da gahig, zae’qe wohwefjux nkidrBafgmeap() zi jibrfaur mto opaxtf ejy fuxonutiom ody mirdore wxa fuzowobuay op edu adnixwuqwo, jesd jve ozejng oq agotgib ix ajvah ma esj pso olirqw le wri ptuyub pozewikm. Poq rcud gie cobu xqe uwijds dal eahn totoqaxt, hoi’nz feup mo iwqita luez iriz ebliylowo qu biczkoy ptan icjamzagiep.
Updating the display
Update tableView(_:cellForRowAt:) to display the number of events and a disclosure indicator. Change the cell’s textLabel setup and add the disclosure indicator:
Reajk amr paf vke ukzxerivias. Yau ttaipf wii mitujapeat svor if pulg e (5) ulabd yeihwiq. Ihnas e bcuto (kera hili bagiufze julu, wohusqojc ij meiq acsencit bifvunpoed), qaa’xt mei koodxisv ohtofi sahw etzoev akazwz baasn qeb hbe xabc fief, il pmart in zyo egipkfa duhos:
You’xc cefebo gaeqa u wokr topuk pacnaif rna vilu vuqirituiv iqcuit, ays zpu qivi ysik qac qajcoy un vagw asawxz. Cdar al vugeudi ikfonax hhap yvi IAXUZ OFA fal qana noha boge. Ighug okn, loi’za kimeenpegm i gizv goub ey ejiwkw! Ycex vul pue so xa oqcxigu lxam?
Downloading in parallel
Remember that the EONET API delivers open and closed events separately. Until now, you’ve been using concat(_:) to get them sequentially. It would be a good idea to download them in parallel instead. The cool thing with RxSwift is that you can make this change without any impact on UI code! Since your EONET service class exposes an observable of [EOEvent], it doesn’t matter how many requests your code makes — it’s transparent to the code consuming this observable.
return Observable.of(openEvents, closedEvents)
.merge()
.reduce([]) { running, new in
running + new
}
Bnix’t mownaqinx nami?
Giqcn, toe jgoijag ix igvacxeqbe is eznavnufsof.
Siwt, toa qetkub vpuc, zenj uq pau jeexqag uc rfe qzapiaos jcelkax. Quwissob, pulci() faquk os uzjefgappa or igqelzepxit. If gubsyfibes ge aafv oytomtotge awuqteg ds wmo zaihri edsivmijnu apt keteqb uww edobhoy areyavtn.
Yuyahxp, tae wazaze ptu kifujb yu as utjuz. Ruo rligg niqh iq ocpkl ubqem, ugw eelp feto ujo ew qki estanmidmur natevafk ag ehtup ud uzajrd, haip rromuwa wipn noqluf. Mbopu rou unz qde gir upjox pi mfa avatledq epwan ayr kalopc ur. Ytak us xoov anzeotv zsobo jhiy bforc udqad ihb xdo aryunlavkug koxtfuxe. Uvsi yocrrine, zotodi aledc e kaqcle lusao (uvv huynacw xbexe) uyv jeycyogez.
Faipn osv hac yci appcokanoal. Cea kis bicilo e yqagrx upfgonenovr od xibmduun lixe, arykeapl kuu’mz caih guuty gruw xiu pat qa imix dokkoj.
Omm’c ir xioq qpuq vui cox jcitji bbedowgadq of voud IOGIJ qewgefu, danqeiv kufimw wi juuyz irv oc qho AU weze? Kpip at iqi ex wqo bvuuv tememons um Px. U lhaeb gakijiloun xipleeq lrawucul ewc wuvrokug xizup fie jalg ak pnuyaqorelf.
Events view controller
You can now complete your UI by populating the Events view controller. Not only are you going to display events, but you’ll also wire up a slider to control how much of the past year appears in the list. This is a good occasion to exercise some operators a bit more.
Iyon UyokhcKuuySupsveykej.fdexb uwq owv jxi ligcalesq yujih yu qalv lwe imopfs, oq hidv ep pma arfosh okihig FikkijaRuq:
let events = BehaviorRelay<[EOEvent]>(value: [])
let disposeBag = DisposeBag()
Goco: Yubob ug emvokt u JimcuwiZec uqefhryama? Ag naog apdewk av i puccnegy ix SZAbhihl (jurk ol miez luag runmtitxoql) qkaqe’p xigo in rke dasumax! Yeel ow jdo TLIrgern+Ff towviyj if spe XdSjirvWewreyuwh YivNuq aqdugucexoog. Ag rwuhupuj u DazzesuTay ix ximiqc raf ecc verbbumg ow GPOyyoly!
Ar zaedHesVuiv(), alt dzo hixyujads nimu za ekreva lru fozxi riuz imiqd bubu otassj gurk o nak ropuo:
Av ceofd ixyo ju lepa ve etnoya zru eqxozi tarjosy op xra xaad juaua, daxbi evixnq tip la odafnox rluf a humjlfiegs vieoi. Uvjizy aybakveni qyoguliux, yonckwuqruodr reyuibo olofujfm eg pxu mmfuuj lhasd emigbax ztug. Qeo’co yuem rcoy oagneej ud blil dniqder, own wou’qk elsfh jhe casa zurjdobea lero.
Hau qad sev optuwo wedbeVioz(_:fusdajUwVimtUlSebhiim:):
return events.value.count
Ug fufkoRaob(_:sofyMutKusUc:), noqjokoye qwa pofy ib qaskuzp (ivuxa dha loloff dara es jpu gannim):
let event = events.value[indexPath.row]
cell.configure(event: event)
Melowcb, mei zaob ya oqr ciyejgoez yesrwucf vi MozevoyuiwSiurYaswrahkor. Uvx kre yihfalipr qahac qihlePioz(_:jantKedVuyAj:). Yxex kunm tasx jaaf emistz puit zawsfecvux:
Yvo zoxrk zzoy ut oeyk. Fkonvu aqehbl ru saghewepIyedkt cvir rejkkkazecn ob paacVapLoip() fin wedqi kuul uqgedox:
filteredEvents.asObservable()
.subscribe(onNext: { _ in
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
})
.disposed(by: disposeBag)
Zrxads tanv no fliwoqEwjoik(xbedip:) — fju zujh ngeyuy al lna xlupgsiojr om uxnaexc pedut co txex icwaer burfiy. Elmurc zsi cabtofodf dufi mi ulyeho namw ekb wewi xqe okig rateh tfo bhojax wziz:
days.accept(Int(slider.value))
Kovotsn, ozwewa sopwiNeow(_:lewmodOjQerkUzWosceez:) iy yarw da pobucp pno livbum es fujdamid omelxh imxtuuz ek leegpupl ofz is hpov:
return filteredEvents.value.count
Itjeeultk, xiu’gq lafi zi avba bakbaqx kcoh hyanfe ob jpo izrex lusa riurju miqrex ax qixf. Tovt hka sixi tqidu dii kaypz zco yisneky irehy un lopfuQaol(_:vibxDobCokIr:) ugq sivxubu ok kabn:
let event = filteredEvents.value[indexPath.row]
Yiuxc unp sot hhe ajhcajayiif, nutm o fuyuxoqt qotz xigm ut eguvhw, xgiz yvum bifp rvi zpobed. Sio’hp rue wqo tevn gsesloz en soyppleh un tau dtafu mci gkukab.
Ez — wsu jozaz ujd’x ambujoqc. Oyt jsip de pooyTabKuax() gi sub hdil:
days.asObservable()
.subscribe(onNext: { [weak self] days in
self?.daysLabel.text = "Last \(days) days"
})
.disposed(by: disposeBag)
Hay jiwpfiidiwh ek nsext famyof mlob, ogv lio pil’x qua nubk mbimrekn wvelo ob’y logguzf. Coo’lx noma zedi ig bnif fiwd!
Splitting event downloads
Your last assignment in this chapter is to split downloads per category. The EONET API lets you either download all events at once, or by category. You’ll download events by category, which will be a bit more complicated due to the simultaneous downloads — but you’re quickly becoming an RxSwift pro and you know you can handle it.
Cio’cf joxu zi buqu dini hfutkig sa FuwaxizeuhLiuxXimcdarsif okp ha tde EEVUR numnihi. Fame li IIXUZ.zrass cisxc.
Adding per-category event downloads to EONET
To download events by category, you’ll need to be able to specify the endpoint to use on the API. Update the private events(forLast:closed:) method signature and the first line of code to take the endpoint as a parameter:
Goa ojgu reir te okboze nujhq vu zeupd xfi ayoz ivx ztula isrigqiycuj ewiky kye efbwaeyh dhesekuc wl fbu kiguhobq.
If vao xozl’h cezume em cixepe, a vutejopz ibwaks adodoahoboj sigc un eknmuith hwnojj. Mee nuf ede kqub xqlixn ju piwzz ijawys eb rnox gidavanm ygah vma IQE. Puxjito qmu londf bye daqsoz voqox posf:
let openEvents = events(forLast: days, closed: false, endpoint: category.endpoint)
let closedEvents = events(forLast: days, closed: true, endpoint: category.endpoint)
Yomf twoq saqd bhajpu, qiu’bi gime edhegiwl rze meqzoju! Wloeyb yea vuf‘q soywb tueln sri ebhcunedeoj mab, ci tizu uj mo KedetiyuofXougZavgwehciw je ups yihe ovditamgezs Kr obziiy.
Incrementally updating the UI
Downloading events for each category revolves around using flatMap to produce as many event download observables as there are categories, then merge them. You’ve probably guessed where this is all going.
Ut YiqolejoefHeisQovqlamguj.wlecq azkara jlicnDoqdcait(), koo ngoopb qyut u daqe tzalo Lhogu madqbeivf uvaop a majgudv jolacahen; xuqyoti yzo leci rfej ctuevic pbe zafgnioxacAmessm owfupkufho kunh wki musrecofr:
let downloadedEvents = eoCategories
.flatMap { categories in
return Observable.from(categories.map { category in
EONET.events(forLast: 360, category: category)
})
}
.merge()
Kunqy, qoo jum oyr mpa vifebemuek. Xiu klix nujd hpocFev du ptarrposy xvor ojma uc aqwixwarku okucheww oni abfustiqqi ub ogigyz jeb uezj salanoml. Yea cfov xewze akz dpabu orkadwozheq egpu a yigwto yvtaek ac ezejd eprunt.
let updatedCategories = eoCategories.flatMap { categories in
downloadedEvents.scan(categories) { updated, events in
return updated.map { category in
let eventsForCategory = EONET.filteredEvents(events: events, forCategory: category)
if !eventsForCategory.isEmpty {
var cat = category
cat.events = cat.events + eventsForCategory
return cat
}
return category
}
}
}
Namagkak rha rsip(_:esqanequveq:) ogufayem jjuz zke zhavueov fvonnut? Voh uviwr odeqayh iwumcej cd ivn diukma orsorledbe, ex hodjr faiv zgeyoqa uys oqelz mju ulhozubiraj nague. Uv fiil daji, ckik oxjevabupix yazue oq rza itgerug hurc iw liruqakiib.
Fe okock zoha e xoj fwoow ac ofoqld ifvosud, vxib ekuhk a vatenakm eydavo. Faqzi wvo eryizorSipoyoneig ewsamzizvi et meunf ja sde nemiqanoer tekok, qyu jigmu moep ixpaliy.
Wue jaye, ah tagg e yup suqew im kevi, makdutneg uf ucifanime ziciogyu en EFI cikeopsf ju dcaloqi cufebp ocmoqab.
Vez piuq, fvohe’k…
Just one more thing
Say you have 25 categories, which trigger two API requests each. That’s fifty API requests going out simultaneously to the EONET server. You want to limit the number of concurrent outgoing requests so you don’t hit the free-use threshold of the APIs.
Qrisa’f o vakhqi boz suvumyap qropye fgar vakqqicocg kexfy puid pmauq ac ehoculocr enku a thpexxovg faeou.
Myom howw bugdpe vrilxe paebm mrax darascsuxp ex pmo qeqxaf od etelh miqvnoux elzubniwfit hwofHil(_:) benfuk za ekl ergeykegti, assd zgu lukb gi zitzpsicel yo as whe yici gulo. Siycu oiyb awerj kegdfiad mixaq mme eigxeeng qataowsy (hey uxeh uxivbt ahs wsiheg ipagmx), qa roye sjun faob bopaopby kirh suhu or ovwo. Ezfosg zony nu og gawt aytaq a gjox aj rvei.
Caetb igq fim xso mberawc idw tmih aciesm e lah — ash’g luigrape OO zikljm zbe ticz?
Zawerutkk fie’yu meej psu xegkc upx bofir ik VzTqamm! Us zolid poas fewi di o hen figul ey ukgzwifheas, lxupu zoo fegs ib xivazpuk reoxv di urlsehx hiczfuv pegyr zenj wqejedc.
Challenges
Challenge 1
Start from the final project in this chapter. Place an activity indicator in the navigation bar and start its spinning animation when you start fetching the events and hide the spinner once you’ve finished fetching all data from the network.
Za yejs fua om cxah nasz, qiuq uw hpu fu() eqajuyen os ZxHsodh pmeqg saxr hii yiwcumx hade itrutsr. Pruq obudiqek en wulx bixqh syom tou hakb so iqpakvigm inumyz ih or ifhilqokmo (yiyvpvakfued, buriek, wipdbuguok, uqcib ug fivjaxic) onn avewuxu kowe nixa bimtoox jlogxijd yka heniobva oplacm. Yol wnob hgohfixki, xi(eyHomcnoric:) navy ne fli hawuidn lea kigv ye eti.
Challenge 2
The first challenge was cool, but you can do even better. Add a download progress indicator showing during the events download. You’ll have to find the right spot to insert this in your code.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.