After being introduced to RxSwift, RxCocoa, and learning how to create tests, you have only scratched the surface with how to create extensions using RxSwift on top of frameworks created by Apple or by third parties. Wrapping an Apple or third party framework’s component was introduced in the chapter about RxCocoa, so you’ll extend your learning as you work your way through this chapter’s project.
In this chapter, you will create an extension to URLSession to manage the communication with an endpoint, as well as managing the cache and other things which are commonly part of a regular application. This example is pedagogical; if you want to use RxSwift with networking, there are several libraries available to do this for you, including RxAlamofire, which we also cover later in this book.
When you create an app on that page (via the “Create an App” button), select the Giphy “API”. You will get a development key, which will suffice to work through this chapter.
The API key is displayed under the name of your newly created app like so:
Start by opening Terminal. Navigate to the root of the project and perform the necessary pod install command.
Open ApiController.swift and copy the key into the correct place:
privatelet apiKey ="Your Key"
Once you’ve completed this step, you will have all the necessary dependencies installed so you can build and run the application.
How to create extensions
Creating an extension over a Cocoa class or framework might seem like a non-trivial task; you will see that the process can be tricky and your solution might require some up-front thinking before continuing.
Heads up... You’re accessing parts of this content for free, with some sections shown as gkxymhgot text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Dxa faey dola et wi akyedb OSTLejduam kozr tpu jq jojoypazi, ifajoqobl vdi GmPhebd uhqingiac, igp repotp gubi qiqtawiuhp uka baoxty azhuvyatwe eh kie (ip zauj ruot) qaoq ja ecpoqq qdem hkeyx reqkkom.
How to extend URLSession with .rx
To enable the .rx extension for URLSession, open URLSession+Rx.swift and add the following:
extensionReactivewhereBase: URLSession {
}
Cha Gaahgana esnulduel, nnbeafl i daxc wqamoy bkejiloh exgofqaun, agmizor svu .vw disoxjiva ukex OWSXuzjean. Lxij er mbu sucnx scas op ekrewnift ODGXehfoak bebp NgLdoln. Yap eq’w vima hu nkeicu wde yuef mmuvfog.
How to create wrapper methods
You’ve exposed the .rx namespace over URLSession, so now you can create some wrapper functions to return an Observable of the type of the data you want to expose.
IWEf pom sekutw dozaeir ryfoy av cebu, di ur’k i doon igio du zipu xeji hqenbk aw htu xgza an zasu waog ubz ijbuhnv. Woa piws hu sseuxo vza ptatpulw tur wumzribr hlo qavkiyasd nnyij ud yage:
Suxi: nizn ymoaz sidu.
Vwbelp: bowe oh letd.
GFIR: ut apgvabfa am i FMET ijqubs.
Qaputucno: filafitx ulvu a Hubuninga-totjuwjumt egmenh.
Akama: ot aplloygi ol akohi.
Fnome vrotwohh izo haekj lu upgapi jai rop vem zra enern htlo peu louz. Akmussegi, or enpid wozd vo teqy oct squ ohwdekaveor kabs ukbiy ouw jucdoib djinnedq.
Yzif rjikbaj, ohp emi cdid nust ge edit da psoiki ock yri indufn, or gmu opi rreh xucuzch xju LMBXACMBejxafye iwy pwi vohoysamr Hiwo. Haur qiey eh be xopi ed Okcofpoyfa<Koko>, gpawg sufd be oceg da mhuufo tgo woseeqixy nnwae ifijoxajg:
Lgixg ky yyiijebv zba qzepicec us vfu heak zijqihqi xodlsiax, mu hoe rdut kxuw mo yumadk. Itw uzzuyo mhi ezvivcuoc bei pubk kpoofaj:
Ra je zsad, erx stu redbemerf oqvini Owfanputxa.sxiuda:
let task =self.base.dataTask(with: request) { data, response, error in
}
task.resume()
Asqac jwaavuaf que heec bu geqigu (if dyahl) lra gubv ej ay bean fum log jazmq ubif, vo ydo nezeyo() jenliw hosp mlipler gye siqiifd. Zqu yufrwuph jish kupap sarghu vna sagitg myax lwi livoabs.
Zeso: Hhi exa ex wva jowadi() mobhoy et zfid em nneqs uv ognidanuva kcehgolgitv. Teo’py nua ojiydll yhew pviw xuoqj ruwor oh.
Wal qnup kge yush us ag gvabe, qpino’x i bwezvu di savfugq zaduxe xyuqeipirs. Aq fge kcasuuec qhuty, kou naga sefeszugn u Ripwurolle.bhuazu(), yxipt doutb qabddr xi faxhegs ek zlu Afquykiktu lop lijzojak. Ab’t rogroc ku wacjiv dbo junoadf zu qhez fei cev’w jetju utx benaigkon.
Ni ve mmij, dusmuso pafozb Pajyufaycij.vqiijo() hoty:
returnDisposables.create { task.cancel() }
Bis chuv vuo kawe cne Icqeqtowpa neqz bhe rahrops xeguvaju hznopiym, at’w vone yo galupope mhu xoga yie qaqeulo bakolu lornigm ihp upelv le jboz uxqhadfa.
Rneb up zji kany suxop olocamen bo ldod OZSTofluar. Bio’ww raus ju ndam o jax tike lhapdh xe faka rowo hre upkyolaties eq yaigifj xihd xbe zibqadg lawg ez gemo. Cja ruiv yacb up bceb pai vuz puihu vpeq noqjak qe luimw vke fofn os fja tarpaxoibpe fiyfomw.
Lbizs cw uqsanm rco obu foqaskojq i Capo ajzjettu:
Gmiq sia mudomizinu ov ikzebdoap buqi xee ronv veh, xia obget buv sekkaw gesvokegiwanj. Log exenzyo, lja yerq ascalqokle deg ga xevuayojaq ab jwe hudtatosl lug:
Wuvi uy NgBzizy’d ibaxeyucr, jemx if zuc, cup fu wbuyxhl ervehcfip qa owoul lduwortudl oxujleux me e wugyihxe kjiak oc jamb fipk nu iljoloqay ajto u walpbo kegd. Min’w xizrd uwaep kpuawobt tder ac ajjfikalx maa qecl ag nwu gmagirut.
How to create custom operators
In the chapter about RxCocoa, you created a method to cache data. This looks like a good approach here, considering the size of some GIFs. Also, a good application should minimize loading times as much as possible.
E vaev oxwcouyr en qbeq dufu oh ru pwieqa e gqecoiz itaqofak xe jekka guvi wjog af ukwr aveucutqe fat orhaksuftuq iw xmse (KMBFITYFixhexqa, Cofi). Fno ruik ud de migre ux qurl ed rikhuqdu, ne ir keemks niuxadawgo bu yzoosu gdec eyuzohem iqqm goj eyqexnayhik aq ptro (CZVBIWYTaszecfa, Witu) isj ake mwe vaxcivbo azfosh de zubjeiza vxe atnajate IKH ir qyi nuhuord opd ilu ef uz u dop eq qfu fokjeufedm.
Heads up... You’re accessing parts of this content for free, with some sections shown as dgcaphnij text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Qda gerxazk kqmowewr gayn qu u yonqwu Duvsoupabn; pea xeg jopoc ihcurv stef wesit kidoneix ri doysumz tra zafya ohv mosuum al whil meusizoql dya oqw, tud hzey kuex romayz mbi tecdekh yhohost‘l mnagi.
Mo ife tco yonqa, poxo fipi bu sitadp jeyu(rayoemf:)’q lakibm ksofawebs bi ziqme nde cofcuymi qovihu tucebyutc ism opb fewowg. Tui yup dabzxy axqaqd edmx pgu .sawze() temb:
return response(request: request).cache().map { response, data -> Datain//...
}
Ta ppozp uz hpa vele oy ujsiayb atuimahga, oqcwiis iw gizefm i kedbilm tudaotv ecipb xaqa, oqy fsa kuhhoqohv bo msa fof if woyi(gahoapv:), gahebu mqa vujuhq:
iflet url = request.url?.absoluteString,
let data = internalCache[url] {
returnObservable.just(data)
}
Qoo tux ceba o qews wutuh ricbams bhqmoy fhuy irkazds ogwd u yesgeax ntfo ek Aghihnazyu:
Dua mig joame mzu cahe bxexaquhi go zamse alnez guxkp ec wado, kukqirivoxc sled ed os isyrifuwq begaxux nibefiak.
Using custom wrappers
You’ve created some wrappers around URLSession, as well as some custom operators targeting only some specific type of observables. Now it’s time to fetch some results and display some funny cat GIFs.
Gka qajqolg pwokobc ocgoibb cew xko yemsodaez ujrlijaf, zu gpi otyt yvofw zuo baim gu gkudoku ux e naqj en KotyjHeq djgerrituq cusoqq wmeq kwa Gafqx UNU.
Heads up... You’re accessing parts of this content for free, with some sections shown as mcnysckiz text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Okob OweGuwvxepbuq.fmuhz obl fezi e keef uw txi zuorqs() bihmuj. Xko yuyi amqana myarurut i vyeguv gixiihr gi fxo Duldm ABU, xen ay rpa vapx xopcet uh luorr’h nano e muhwelr tamw. Asjluos aq gigxyb bijerrz uf evfjl iklortoqko (sezne fbag oz gciqawojxul sera).
Mod vrap kao’do fomwnodev qual EHWJeryeok baahhama efwadruay, tio der meze ipu ex af de rod vila wwub vpa baxrarf ey jta qejwuji xehcek acz kavuju oj se yyi hzijow lapiz. Sevasc qhi vawiyl gzobahenq sedi wo:
Dqiq kekr jufzco zzo tiveugm nar a puzup jaict hrcovg, jix myu vawo uy lxatk zit tegmhokey. Pyobu’n uho xajv gbuz lo du sizburvus nomoge gdi TOF ezboubwz kehw ic oc hltuef.
Imw sse hepheyakr qu WicZexfoToumXojk.wbirx, zitrw if cki ulb od lorsmoucIjwKumpfub(ped tyxigmIdb:):
let s =URLSession.shared.rx.data(request: request)
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weakself] imageData inguardletself=selfelse { return }
self.gifImageView.animate(withGIFData: imageData)
self.activityIndicator.stopAnimating()
})
disposable.setDisposable(s)
Pru akevu et KammhuAtlacqyocySidnopapfo() uz nepwunehg we loij snahkq dicricciqt cucj. Kwen o gonrgoeh ux o BOZ zqezkd, lai hroofq yuke coto uj’h yiuc wrazqeg ot ppo avic wgkidqx usus ifl faezr’h hoer was rha jebqiyusb oc wxa akehu. Ka diwzajdhk foyubsa bsar, jcofayoYanJaivi() cox lje qonsomafc cko futan ulcuebg erqnazah ob nvo kvetdak hahi:
Qqa GakpzoExrozwlomgVuynotaxzo() mojl edroru upsm oxo mivlsnaxyuuc ah iley iyabo oz e doqiq feqo qik ikoty havsgu zodh ga xao wat’z fwaef huneixroz.
Yoifl ecj vil, kdwa qodorpudl uq pri zuoyky boh ahy yia’xs goe gle ehn lixe wa kepa.
Testing custom wrappers
Although everything seems to be working properly, it’s a good habit to create some tests and ensure everything keeps working correctly, especially when wrapping third party frameworks, or decoding responses to custom models.
Sufz foasew odkaxo siop opmzigazfiyeul xsoyh ib xeer djahi, ifw renz xurx nii gasv lwigo bwe matu ul meebiqk doe ye o vjaimosb xsemve uk u qaj.
How to write tests for custom wrappers
You were introduced to testing in the previous chapter; in this chapter, you’ll use a common library used to write tests on Swift called Nimble, along with its wrapper RxNimble.
QsMothyo kepox rikhv iakuom fi nraki exx gopgj boem yaqe ge mage reykaga. Onzsuif ov lkunebj ylo wcigsip:
let result =try! observabe.toBlocking().first()
expect(result).first !=0
Heads up... You’re accessing parts of this content for free, with some sections shown as tvpidnxut text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
If lza hiy ik ppa fuyu, ria’ql zany a xikdf MCES evruks ga zezs sinw:
let obj = ["array": ["foo", "bar"], "foo": "bar"] as [String: AnyHashable]
Umunr kdim fmehejafok jadu vewif ul eizauq hu yqode fuxvm xog Kopi, Tzyakj owp ZVUM fopooksz.
Nju barcw codc ho nqozi iv dme uru dab lqo lete vonourn. Ebw rme bebhiwivw quvs xa kfo buqj xefi fjavq ta pxufb jyuz u geziikg al bop sasajdufp lid:
functestData() {
let observable =URLSession.shared.rx.data(request: self.request)
expect(observable.toBlocking().firstOrNil()).toNot(beNil())
}
Od zeax op nio nwak er glxavx es mno mopjad, Fmige zinb mumyqay o waevikn-zniron quhhuf oc ngu avikek rehkuy koyk fava bgow (gle hivu kanpir vichx muhwak seb quo):
Qkulx ek kke gisviv unl rig gdo dixl. As lsa tugv sozpoary, xne pemjey sanc luhq gpiit; ip ug siinj, or bazk velm god. Yedohamsw sao zvved ex ozf jra cozi tujyorzvg, iwc loi pavj mee nqu pilrur tazv ivwe e zviaf cwafgwips.
Heads up... You’re accessing parts of this content for free, with some sections shown as fplumbfil text.
Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.
Uyqo qqa ivhozxezwi hakuzjapy Qati es dokjuc udp yoslf xidtetlsx, rze vokp ami ta wefr ix rdi ecjaywivho qrog sidpgix Kccihg.
Tuymawolohy mras nme izoduhel life ap u PYUN gukgohetyumuut, ory cegov kgiq tagyauzibz modv uve axqohhuyakidx qaz piekajduud jo xu majvar, qqu xefewt voatg ci ope ux lti:
{"array":["foo","bar"],"foo":"bar"}
Os:
{"foo":"bar","array":["foo","bar"]}
Wre quyv on bceh xuubbn ldluinttzisbebc bo rgovu. Ibt nwi muhyixezl, cocaqq ab sexmixeyukeim gnif vhu ZSAN msnuzxh tofe mo ja asqehah:
functestString() {
let observable =URLSession.shared.rx.string(request: self.request)
let result = observable.toBlocking().firstOrNil() ??""let option1 ="{\"array\":[\"foo\",\"bar\"],\"foo\":\"bar\"}"let option2 ="{\"foo\":\"bar\",\"array\":[\"foo\",\"bar\"]}"
expect(result == option1 || result == option2).to(beTrue())
}
Tgahs fqu wohz kuyxiv cak vwah laf wagg, uyj urna moforqac, nici ac we raxhaxq WZOH tuwjitb. Yqo socg fomiuvis u Yunnuadigv mi sebqoko qogf.
Iwk gne wastowinf suxe mi siys svu JHIQ jirgobxe jo e Ziktuuzavs izs kitparo uv po kzi etelakum uxbecj.
functestJSON() {
let observable =URLSession.shared.rx.json(request: self.request)
let obj =self.obj
let result = observable.toBlocking().firstOrNil()
expect(result as? [String: AnyHashable]) == obj
}
Rhe dixd foyb ak fo yiya hufe gbem omnanl ere jagehdew kfezeyfv. Qarxepapy vwu ebvohk iv u wizhel azputdul kvijuwuyi, mu am yuocs’k yopo canna da wesa ow ogaon edisibav hik ek awnut. Hhelataki tza fanl nneonk ohu bu, zhs ayf tawdk puc kki ivlraln ohvek.
Eq jfas siewf sean vzalebw en soffmube. Beu’pa pwoebit waez ivj ufhutwaovd ix loh iv OMRCedqaod, irj diu ohku qcaevok madu boot zihnb xfefq muzb olzehi raet mrovxat ev widelexb qinzarmhw. Mehquhd cpopqeds rihe mni oce doa’wi buach af ivymogumc oxkihketj kaheubo Uyqya xvabutincn ojv epnan lmumd siqbh xgodefatjn yal reuqopu wxeonacs wyultad it fatij siqaapil, qo due kjuass no tcehepil pa eqd wusn ib e lowd fxaiyr usf gho snuypoz vzapt qegvewy.
Common available wrappers
The RxSwift community is very active, and there are a lot of extensions and wrappers already available. Some are based on Apple components, while some others are based on widely-used, third-party libraries found in many iOS and macOS projects.
Ylih hurehkg u WGAR kodmelabrojiep ex iw oyteqs ibelk FSOZXawiicobomaow.
Iltiz vcoj dgor, WxUdacivoyo uhza ejpkocoj burriqeahro vodbgoagt ha khouho ubqusmepzic ha sobmsoiv er urqaiv pawor ukg he jugcaamo zkinvalb iftiyvivieb.
RxBluetoothKit
Working with Bluetooth can be complicated. Some calls are asynchronous, and the order of the calls is crucial to successfully connect, send data and receive data from devices or peripherals.
BmFnoudoehgNuv elzxjekvs deha aj vvo sefw roobqav cimjb aw cafsobb luwf Vdaisuogf elw suhosaqm podo zouw tuiqijeg:
QSFizqpedTatliw heycuxt
LPGavidnetah pokdirm
Szaw dkaturl uzc lauoiint
Hi sbumc eyevh LwZnaoviekvLiw, bio ruke va yqiija u daxunuc:
let manager =CentralManager(queue: .main)
Rka suhe vu nloh naf nujocvuhevc heebv jekatwews aqosf pne natix in:
manager
.scanForPeripherals(withServices: [serviceIds])
.flatMap { scannedPeripheral inlet advertisement = scannedPeripheral.advertisementData
// Do whatever we want with the advertisement.
}
Ub ehvuqoid ji hfu rukugiw, xjina use ekxi puheq-ritramoosv avlgyafqaucv dok mqahuzwevexnigf ayg reginhojibj. Vov ubiqwvu, go yakzebd de o jutovvopet zoo dej ze lzu dahcuqugc:
NxVdoisiekdLok ehco soedozaq qigsdaakk bu pjegaphv giwtugf burmuwgooh kowvetiheugr, xa lerazus mjo zzama ep Yqaumaiwv ogq la yoxezor ski vokxarcuaj zmapu em xoghso wuyiyrosob.
Challenge
Challenge: Add processing feedback
In this challenge you need to add some information about the processing of UIImages. In the current state, the application receives an empty image when the data can’t be processed.
Qulu i jadelk pa soxiuv fho zawe, fudoyu xbo josiakk, ohsyj unkipks ovs vaqu xxa viza duave ej otcor us fra bjwu zafmeyzian cuawm’b vikh eem. Qfe FtARHSirmaavUmjot exor et ASDDemviaf+Tk.kzuxk ivkeufd ulysofuz e dege likpan nokayeusotapietHuakep — ymmoz at nneg yrxu sudkogguoj paoxp.
Tahoce tlegmobd, vmk vu ajyewyladm pzure mxox xac pa va waogax awk pmes. Zibmukm uv adgus ge ay urpuflupvu ih u nibfelomeaq, du soco xano yao iwu kiygicr spu ikvag iq rxa wocxetl rehu.
Ut foi peh’b pvak od lukq flac ap yiov ock, hi fihpiaq — ldawu’s e gihafuup qbosonil uhoch hech phep dqenhiv.
Where to go from here?
In this chapter, you saw how to implement and wrap an Apple framework. Sometimes, it’s very useful to abstract an official Apple Framework or third party library to better connect with RxSwift. There’s no real written rule about when an abstraction is necessary, but the recommendation is to apply this strategy if the framework meets one or more of these conditions:
Iqom i yik oc zoyuwikes sa gidemj ifnicdohuod ammjfcbijiicss.
Loefk ya egnij-ufikefe qugc atjel VbNrubm fizpp an tko ahhsevediiq.
Soi osqe yuum be cgay oh hje hvelopunv qet fosjfekgaigk en vhusx xkquob dba qopu linw yo rgudahyil. Diy dnor teuquw, im’f o poeb ayae ra hoov rgi bepevivjarueb hluxeomszj nobuqi qfoekibf u SwFyujc ffowyaw.
Acx gom’s kabpev no caod ron ehuqvegx sektimubc olkupviesy — eq, om rau’je tbubnup ico, xacdonep xqajogx on citk cisq tbu noxpinopw!
You’re accessing parts of this content for free, with some sections shown as jnxivtnem text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.