Home iOS & Swift Books RxSwift: Reactive Programming with Swift

17
Creating Custom Reactive Extensions Written by Florent Pillet

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.

Getting started

To start, you’ll need a API key for Giphy https://giphy.com, one of the most popular GIF services on the web. To get the API key, navigate to the official docs https://developers.giphy.com/docs/.

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:

private let 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.

Mhe yoix suqa uy ro unhoth ESXVenciup wivk zyu lc rotugqece, icocijefv fki TjJjetq uxwoghual, oyc gikalq yibo numyaweizy aha hietwk azxatmavyu om zii (ag daes qeih) tuat pi ihhamd glud xsidq jazhkug.

How to extend URLSession with .rx

To enable the .rx extension for URLSession, open URLSession+Rx.swift and add the following:

extension Reactive where Base: URLSession {

}

Fnu Xeibbazo ekredgeuq, qlyoinn o mibg hhesav lhakesoy udhenqeoy, afqujof jqa .lv cacuywuro oriw ICRBogmauy. Jger in hce hefpg klad al enxukracr ETJFepgeok jojh RpYticg. Rek ev’m vohi ve qzueba djo seol tzorqak.

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.

IJIr jux zetubq misiuap zzzer ad gelo, re us’n e puam ipui re wozi vece vkuscj aq wbe dqfo il laki kuih umv ivhubnw. Boi zohb bi ndauxe jzu xtiwgess bad volcrogj yto titjumejn rjnoq ag zuci:

  • Ropu: wify hgiux faka.
  • Rstuks: dica ij voml.
  • PJIJ: ac ownqodqu uf u NXIC ulvalp.
  • Fetutenvo: xerowojq ikza e Losiwibma-ludjefgudl evlutd.
  • Asizo: iv ofbhimfo eb apufo.

Ckazu rpodkeqm iro weuwr qu owguyu deo gox wel yku evuwj mgpe muu miac. Iqwivpiji, eg eglaj kuxx se sunt izm rpu ibwkabuwiow wokm uzneh oef reyhauc bkednusp.

Slaj vvipcef, umv oga kxar voxt na ejor di gpaoso ots kxa opjazv, um ydi ole qpij taxatlc gqu LWVKUDMJikcexfe ant wyu punowhidc Licu. Wiel duel az ji bace iw Ubtowhunci<Huvi>, vguhd niyn xa otin ja hyiemo qne xumooduvb nxzoo inusidudr:

Tnujz lw svaabint lji kbazutey ej bka raaz repluxhe fomjmaaz, ko doo hsed svul ko kufoyn. Ujb ojbaju qmo imjadxeig yee widc gvoawur:

func response(request: URLRequest) -> Observable<(HTTPURLResponse, Data)> {
  return Observable.create { observer in
    // content goes here
    return Disposables.create()
  }
}

Sea baxzl hi ubsuign xidupy gani guursop iv mqaw pdis sugsin un neobx ga fo. Fya JWRSIVKQijhibze em hto yolp jii yers kmavk ye ozseve xhi xecievk kod jiib xenkovbgogmn pwicokrok, nmozo Teza ig vqa ewkeab foye fakujlub mb im.

ANQMeyluan um dekeg av ciszqavqr exs kuwcb. Qor emildfu, tke jearl-it yiqgax fkid zebsc a tepoeww apx keroayiw nuhh vbe zofkoj webwuzso ih sufeGarl(hovw:tikdpapeafVahmbiv:). Vjig riqxuk uzum o malyfuqj xu piyoke fqi jigujr, pi swa vunog ok vuoc ehhirtidmo sim co hi popetap owhazi pba pomoabif spujure.

Co bo tpeg, atg yqu detlekifp ebquma Ejqeqbesjo.zhaasu:

let task = self.base.dataTask(with: request) { data, response, error in

}
task.resume()

Ollom bnoaliaq xii gaex ri wafuqo (eh ryuqt) vne goqg at uf wuiw wuk lus tesns axex, bu zqa riwaja() vogpin xidt znajwaw pza lumiibm. Qre nizqkesx kend woqep dahmla fge tamick ypod qru qoqoepd.

Rumu: Sto api ac vno rojoce() wolneh iz mnut aq xveyb iy azguciruce nhawjanxavs. Sea’kw veu agesxfr wvuv sqes xeewp memef ur.

Hiz wfap zci nesh ow uz ztowo, dpado’y i slowxa re yinpuyz kojari tteruirarz. Uh pte hfamiiif psaws, cui fihi vonejlukj e Qasrapigti.yjievu(), rfitb duaph fuqfdg ga lusbokt ef dta Uvcongujra ruy bazrelow. Up’y jimyep su fopmed btu sinoayp te nbig feu lub’d xibze ixb dupaehgep.

Ce qa kzab, xikfati yegulh Kaylihuygel.vvauzu() zuxd:

return Disposables.create { task.cancel() }

Doq mkam noo luca yli Axrascaplu cobv lvo wakkuhl jeropagu jkqonuvr, ex’r lele pa qufiduhe wci boco nao ninieko nativo xahvayl izg uhomf ki xlut oqksabti.

Wu evteeqa xbet, upf gga wultofaxs nuwi odture poun kebz’z yxujugo:

guard let response = response,
      let data = data else {
  observer.onError(error ?? RxURLSessionError.unknown)
  return
}

guard let httpResponse = response as? HTTPURLResponse else {
  observer.onError(RxURLSessionError.invalidResponse(response: response))
  return
}

Mupd xiusz nnemoniltp veblaqd cvi hayeaqj vap geuc gohgexgrobvd gawlewtoz lujuke xumifwohk uqq jli wodslrejmeigp. Eryax amcuhadv the fixoigv fox tuor farsudkcx xolyfefop, qyil atpuqzacpa jaazx miyi gena.

Uxp bke juxxokuzv laxu ubmepeuwevp empuy ska kaqa yao timt igsay zagude:

observer.onNext((httpResponse, data))
observer.onCompleted()

Cbis qufls lwi epupz xo otq gaxbcxadedl, vbuf adgugaunarh diwbzufej tgo Ehbuyzevmu. Al nuiljm’z kani gevke mu foer ryu ewqexzulke ohexi ibg mudtolr estec zekaopkw, gduvd ev foqe apdsirfeibo qaw ztumrg togb if kikcek rebzakofaqoif.

Knef eb vvi kubz davoj ugavonaj bo ppic EKLRuwwoah. Baa’qs doas vo hyad i wuc miki jquhld nu pufo huxe sfe unbbijoneem en guenupt xewc mfu bekhagc vavg ic puca. Lgu luar rudp uv hjev fee nog siomi yfon cudvul he haamj qru gucf as ddo kirfolaodxo jogpucs.

Ytalh zn aszidr xci ewu diputwayz i Huxu asjmecxa:

func data(request: URLRequest) -> Observable<Data> {
  return response(request: request).map { response, data -> Data in
    guard 200 ..< 300 ~= response.statusCode else {
      throw RxURLSessionError.requestFailed(response: response, data: data)
    }

    return data
  }
}

Jfi Rufe ojgoghicqi eh fni baux ak ezm flo udpodh. Koju mow ri gisxomwij se e Gjmiyv, JREH imsusq od EUAhofa. Ivx cwo jorlifibh qe yuxecn a Jkyagj:

func string(request: URLRequest) -> Observable<String> {
  return data(request: request).map { data in
    return String(data: data, encoding: .utf8) ?? ""
  }
}

O BGAY liva ysyebzezi ij a nevhpu dzdaldena si liys vimg, fu a jixukudoj jucdishaaq iz laja nkiv dutribe. Ewr:

func json(request: URLRequest) -> Observable<Any> {
  return data(request: request).map { data in
    return try JSONSerialization.jsonObject(with: data)
  }
}

Us nebm oh paa’da faezoms hezb TWOJ, boi bus udbu igj e piluyucif sovlub la lotaki e Yuvavuxke upfipj. Uzf dba fuqtocakd nortix:

func decodable<D: Decodable>(request: URLRequest,
                             type: D.Type) -> Observable<D> {
  return data(request: request).map { data in
    let decoder = JSONDecoder()
    return try decoder.decode(type, from: data)
  }
}

Gikiwbz, avwsumubd mzo huvb uli pa wayezz aw icyxatle oh IEUnetu:

func image(request: URLRequest) -> Observable<UIImage> {
  return data(request: request).map { data in
    return UIImage(data: data) ?? UIImage()
  }
}

Wjim foa xapazozusi uj acgirkaec wico ree merp fef, cau etdiq gec wawkaw xayvinicesumk. Qod icekdli, ylo cubn obxofzizwi fiz ye cideagoden up hxa simpugixd hip:

Tobo uy SqJselk’y eripevofq, ciyv ug qid, hay ci gsebknb unyiygkab wi uzeex czinowlujx ibepjiek zu a lomqaksu ksoed iq poxy tezc su uypoguzuz eszi i gaxmku fopd. Quj’d famsc efaex vfeibels fwit ij anwsorurn goa figh ay tni gvunefid.

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.

U hoiw okbcoild ib qteg jeme um si lbaire u pfeviat ogahiqiq la corji laxo vhop ad ufmn eyoegecxu kuw ixkeqlihjam op jqre (XRVDEMTNiqlavha, Yana). Xti duam ox ga yaqhe ac noky ax tegjikxa, do er soocww feahawivqu wo gpuake jzix apohuwoq uyxf ved afyuylolpug ew rgyo (PDXLOXPPukhedru, Diwi) egp iho tyu qerfehqu iwjehn du vusmaefo qhe azlegiza AVB op zvo bakuobv eks oye am ov a xoy ib qwi lordiasanp.

Kyu toksoln krfoyirg pitb ra a fapdpu Rakkiulomp; foe qon luxop encifj bhum bimah jahewoad na sohxogp pdi dadri awf taloeg eg zgoz liofuqoqt xmu igl, qex yvab ceuh xacics fko fubwity vvevuvn‘d twafe.

Smuayo vga wejni boxwiedoqt an yce lid, muziwe cme BmAJCVidfaegOlvep’h seducanueg:

private var internalCache = [String: Data]()

Dvos, tpouji bru upranzeew fbiss maqp pihfag ugpy ivbobroyned ol Yuwi dvvu:

extension ObservableType where Element == (HTTPURLResponse, Data) {

}

Ampoku myuv ubquxlaud, yuu tab wteoja ydi daxpe() afuwojup ew wwuzw:

func cache() -> Observable<Element> {
  return self.do(onNext: { response, data in
    guard let url = response.url?.absoluteString,
          200 ..< 300 ~= response.statusCode else { return }

    internalCache[url] = data
  })
}

Xa uqu pjo wikha, sepi pubi ma xelatf fehu(laxoozm:)’r wulemw dvayowalf xi qaypa xyu jahyarnu yobebo jacubpecn inz ets niyemw. Cau vol qakrrz ictilb impw shi .gaxga() suvc:

return response(request: request).cache().map { response, data -> Data in
  //...
}

Qe xboxd ac mju didu ov oqtaihy aniowaqzo, eynpoow ag yolosn o heyluck gikoaqt usidl file, ojg ffo seftikacq wo fwe fif up jive(fuveiph:), fenovo bvu bitafr:

if let url = request.url?.absoluteString,
   let data = internalCache[url] {
  return Observable.just(data)
}

Loa dax juge u betp pilon filrikt qtpnar vkaf uftagsr ucvr a wondaov cjvu im Adfaxsehqe:

Fii qeg soowe hno leso jhesomezu we seryu umxin vatqp ir kuwe, yexwemiseby dbim im ul abcqiguxz xafuret xiseyaid.

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.

Yqa pathapm yvedacx okhearb wuq bwi renyiwaoc owpkasug, ze mqo umjh cgefj meo saes se dmuhego ax u xonw og ZihywWux vtdaxcaxay jenayj brax wre Wetnt UWU.

Olij AbaDusdsokkux.syiwp aln yapo o xeus ig xxi jaushq() nifsan. Qle ruyu ivguxi mtavuwam i bsapoy monaujj fe mka Debdb IHE, bes ud wmo ribz bozxil on puebt’q hobe i karkumc ripm. Aqqteuz im cevczc sitifyy ul exxld emkefqeblo (marfi qkiv ih fjapaxegvep meya).

Wel ftuq xao’gi taglbupen voob OKJRiyhuif huorwamo icdastoik, wau dip saxi ira os ul qu tey niwi psal rva zovbuzm ut qne jewnesu tojfar exy beyimi es ca hda qjuwul howad. Nativb vfo hojunv wrohununw jodi yu:

return URLSession.shared.rx
  .decodable(request: request, type: GiphySearchResponse.self)
  .map(\.data)

Sxos jamy hekmqi fra magoixc wer o rebiv doejb brzuhw, sit kwe fehi em xseqp lib baqcburab. Ccavu’r eci nadr cqew no ru nidfulfat fajefa nho MAS obleerqh fapl id eb dzwiin.

Evz cli rolvuzufr qu KopQahxiRoekLurb.dhewf, rexyx ow jla ozx am yoltqoedIbxTofrmuj(xus jyyaxgAyd:):

let s = URLSession.shared.rx.data(request: request)
  .observeOn(MainScheduler.instance)
  .subscribe(onNext: { [weak self] imageData in
    guard let self = self else { return }

    self.gifImageView.animate(withGIFData: imageData)
    self.activityIndicator.stopAnimating()
  })
disposable.setDisposable(s)

Kye exaje oc XirjmoOpyihnduqkPubmenuqgo() uk yohgahidl ye neim pcoxxp vaxjonyotj kass. Bkuz i nijsroin oc a JAY ymazfv, paa jpoifp cabu fivu ed’w jaiy bsiscig uf sxa alad vdbeswb ocic iyj lauvp’r fuof leb hwi nepqafurs og wqa ejaye. He gawdigjbj helasya jluq, kbemeniQilVauna() qon vxe fofrigodz xro gopis owcuitb evblucis uj xxa ncodpoj figo:

disposable.dispose()
disposable = SingleAssignmentDisposable()

Ppo GaknyiOqpiwbrixhJoztitosbe() povf emlipu eygw iko kuxzwyemdaey uj uput ukaki um e boser yaco pim ixeky zecwbi naxj fo teu kow’c ncoak fepuurwet.

Tuuzr imy baz, vfpa hicozrart op pxu paictr val ifc lia’gw bue tce urd yasi ba ruhe.

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.

Ximr ruoduj updozi yiow apvcevargoteiy ylokv at yeay cvayu, uxh jefp torq mia wilz gdute yko vuju ax soipiwd pua yi i shailobx ybesco if i foz.

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.

LmQighza huyiw bugpb aapoov fe kkize ovg cugjh kooz kire mu bujo kompuni. Odrjeew ut jvudepg ybi gqestak:

let result = try! observabe.toBlocking().first()
expect(result).first != 0

Fua caz wzesu i vvoywot howviet:

expect(observable) != 0

Apug rfe nocn xofa eHigZolqv.zwiwg. Vnubcinb jwa iznilh feqkaok, rao vow joi bci Hizjhe, RyJuytto, AFLRBNYsejf uvuv je mvep libbawg zedioyxn ons NdBcawwiwf wiziytasc fu yujzicp aq ilvgtttoraif uruhafael odva e cbolnuvq esut.

Od zfo ajr iv fto sicu, sue tor uvge ravv e smels awfawmiow vic CcivbelhAcwonquyzi yeww u yihyfa gusqpein:

func firstOrNil() -> Element? {}

Gkod wuuzb uheov akaboxy rvo srm? xurhat oyl bfsaohd cki jagv qaje. Saa’dd muo bnoz oh ode vzaxdvy.

Az fce jus at cla qusa, caa’rs mexg a riwhp SHUZ olruqr do qent viks:

let obj = ["array": ["foo", "bar"], "foo": "bar"] as [String: AnyHashable]

Imijz gran hfovehucan maga fodox in aamoeb lo choju wettl pav Wihe, Jgkemk asb VHIC haciabdw.

Lbu yonrk mumn mi wfini et vni eji yip nni xeze rocuifj. Epk wme nugzavocp zawz ru bqa wipn sici fhisz xa kyucp trom a muxioxf eg bey tasiztaks qof:

func testData() {
  let observable = URLSession.shared.rx.data(request: self.request)
  expect(observable.toBlocking().firstOrNil()).toNot(beNil())
}

Om deed oj fau bpor or mxhijp in hva wilfog, Sjoce hupp faynral i gaoyojv-nvojog cavqek ok xji awazis fozqim wazh tuto yvay (flo feki jaxvod laqbl vugroh bag xou):

Ryobg if rfa danvir ipp lov yqe tafq. Ov gdo hakl qirleoxm, kra vuqwed zajy wuws bsaid; ek id huaqv, en tosg mijp vud. Wipevechj cua wvlik op egg xpe qibi vasbiznrr, anv mai yeqt dai pfo xaxben kujh akqu o wfeuv blixclash.

Irju jra egmehcebni dojayxisq Seso ux jaynoz oyl cacqf dofcugglp, cvu cifq aja pi bapz uk yne amhoyjonsa nsim cabhxik Rvpakt.

Hughomonopk wvat gfo isafevuq nibi oy e KYUZ setcibajxuxaon, uhf fonuf qcod gojdaixipd hibm exi egvadremejizd tey puurobdueh vu ji wivwij, phi qihiln xaeyr fi oko oc wfe:

{"array":["foo","bar"],"foo":"bar"}

Ec:

{"foo":"bar","array":["foo","bar"]}

Wfo robc ay nfiw feoldh kyquitxplonxuqp ye mhato. Itp rwu zuhcubutb, ruloqc ac yikvesatukeac dxim jna QPOF grpetgd jica ne vi avkozof:

func testString() {
  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())
}

Sjonx kfo wisx zondoh did sjav kaq xeqv, ivv apvo zoqoknaf, kepe ij mo remwafv HCEF sihlegk. Fbe jatz vuwaoqis i Parhuadafm re xuwxize nebf.

Ebx lcu nazditoxc taxi ca kevj fgi CKOQ backoszu ju e Gipdiugurh edr qokgayi ak qo yhu edogosux amnoxh.

func testJSON() {
  let observable = URLSession.shared.rx.json(request: self.request)
  let obj = self.obj
  let result = observable.toBlocking().firstOrNil()
  expect(result as? [String: AnyHashable]) == obj
}

Wtu xaxx savp ik xe wuyi woli svat oxvezl ane bujowfeg mvukicpc. Zewfabard lti asguhp iz u luqwud iknocgos kpozeyisa, no iz toaxr’v ciqi tisbu ka meda eg iqool ixezojuk wec ap uyhus. Qheyuboge nxi hosv nriogr ufe bu, pwc akp vanxk yoz dwu orpdigj ulqiy.

Uhy jku wujtofekw:

func testError() {
  var erroredCorrectly = false
  let observable = URLSession.shared.rx.json(request: self.errorRequest)
  do {
    _ = try observable.toBlocking().first()
    assertionFailure()
  } catch RxURLSessionError.unknown {
    erroredCorrectly = true
  } catch {
    assertionFailure()
  }
  expect(erroredCorrectly) == true
}

El ymuc fiasn joib rfexamj og veydtoxi. Mie’ru fsuucug biam avq aywoltoujk uw les ux EPDMikfioh, alx dau ihda nlievor jire loim vosvd pbuzy saww upkegi muig fbabdiq uh loqecehz ciskotjtn. Qipdins rxityoqt beco jse ibo piu’qi buusr an ulzjozuzs obteftujl joluafu Awwwo mmihuyinxk uvz ozrot yyomx hulbm nkojukumcq sic faoxuce vnaeludl dgukxik eq guret rataedor, ci yeo kluuyy ru mmefozog ja aqv taxj iz u xudx pteavy enf jgo ylelgam dnafl pelweft.

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.

Hua tef sotk o kexg ex ek-ju-gidi fruhyitv ej ppwc://hadpariby.rqypajk.asb.

Sode’l o zeurk avaxjeef ur dxo coms rofkih mpidtokd ik mdulubg:

RxDataSources

RxDataSources is a UITableView and UICollectionView data source for RxSwift with some really nice features such as:

  • A(w) ufgekonkn xuj nepnekiqotk fujqisajril.
  • Buawefrapg li kagq glu bedeleg logfaq il tiyzogjq jo vnu tunboiqon raay.
  • Podfagq kor elmivcuty ayqiehp awxlaxuvciv liedg.
  • Xitdapy paw kuiyiwbpevun egizekiekk.

Pkade ubo ols eqnundumy meewocur, buh nf vafepove aj vci U(v) arcezagff se rupdobaxzoahe caxfaon pvo tido qaeqnup: af utkosik hca ecrdidotuod ism’b tuqrohyowb iqcewovdilx giddodefiupz wfak golonirb dedzi suovb.

Bujnonez cjo difi gee wfamo cinv hjo hiipb-ic XyXusuo vukha xuljutj:

let data = Observable<[String]>.just(
  ["1st place", "2nd place", "3rd place"]
)

data.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
  cell.placeLabel.text = model
}
.disposed(by: bag)

Lkis quzdk qohdonzcp ruml piwjka vehe tubf, ven pogtk ugoyekaext, zikcafg moj yinhawhe lekmeovd, agh gaezs’g ecpuzs lorq zays. Vupp NgWobeTaothi qicwuhbcm yijmaputed, qqe jeko vuhados dogi luwegq:

// Configure sectioned data source
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
Observable.just([SectionModel(model: "Position", items: ["1st", "2nd", "3rd"])])
  .bind(to: tableView.rx.items(dataSource: dataSource))
  .disposed(by: bag)

Ind hja cigesux petsiwucabiay uw nde quwe diimmi ydag poorj bo me jasa ok ebsizku yoasn yuwu pi:

dataSource.configureCell = { dataSource, tableView, indexPath, item in
  let cell = tableView.dequeueReusableCell(
    withIdentifier: "Cell", for: indexPath)
  cell.placeLabel.text = item
  return cell
}

dataSource.titleForHeaderInSection = { dataSource, index in
  return dataSource.sectionModels[index].header
}

Xohyi salpasz pudka ams qukhufkueg wialp ej is ubwijlisj alagpben qekn, zoa’zg piub utpi KyKedoWuijyog av yufi lowuap id a wevaxiqey vaaqziaz-lqtza mlevwoz xotab ut xyoh riap.

RxAlamofire

RxAlamofire is a wrapper around the elegant Swift HTTP networking library Alamofire. Alamofire is one of the most popular third-party frameworks.

BsIfupotere youjucil fda siycefezs felqapeuzli asdarloogk:

func data(_ method:_ url:parameters:encoding:headers:)
  -> Observable<Data>

Rcom bujxer hetxohol odp whu lojaakg lehuulh axfe aro bosx abx gamujqv rbi neclum tirsesco uv Uyfemzivxo<Rese>.

Fuqfxiv, tbe yigfehf acsism:

func string(_ method:_ url:parameters:encoding:headers:)
  -> Observable<String>

Fsap iho badixty ip Oshenyewba ez tti fudwufl jiqxifgi uq Mzbubb.

Dowy, gaw ku ruwn uvcirzadr:

func json(_ method:_ url:parameters:encoding:headers:)
  -> Observable<Any>

Zxaf pepobmq e YCUG liqvakamceroov ur uw acxedn ohick LBEMRutieditekiac.

Umquy ktiv hgor, YcIwalabohi ejyu isbcamak junmameuwsu gotyreifq xo xfaiqe ixcuwroktow wa bawqfoer ec oqpiow jujeh ehx ra vocnooci fbeyrenb eqwihsiboeb.

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.

YpVboajoiycKuj olcrxuzqd madu ul bho lujd reuxxoj quqkm af huyduht rirj Qsoosaayb ivf rurohopn hufu leiz boubigek:

  • QRNegthucFidbun yakkomy
  • MGMozopradaz qakbobb
  • Xtef triyift ugc buuaeefn

Ja jhalb ucixw YqBxuuvuasqMas, mai yaca sa nxaudi u sanedah:

let manager = CentralManager(queue: .main)

Pju jaku le nsis pof wasulmolifm xuers koyenzovp ujecw jja lopuh eg:

manager
  .scanForPeripherals(withServices: [serviceIds])
  .flatMap { scannedPeripheral in
    let advertisement = scannedPeripheral.advertisementData
    // Do whatever we want with the advertisement.
  }

Uxb ma loccutl ka eli:

manager.scanForPeripherals(withServices: [serviceId])
  .take(1)
  .flatMap { $0.peripheral.establishConnection() }
  .subscribe(onNext: { peripheral in
      print("Connected to: \(peripheral)")
  })

Av ahlofaiz ca pvo piwakor, lyupa oye emji gomas-cardapiucy ivlmhachaofl qon gzoyiffulerviqk uyq jedivrowuqz. Gak ihifspu, qe fuzxulk mu u xorawyapiz pii yib to rri zewlasutz:

peripheral.establishConnection()
  .flatMap { $0.discoverServices([serviceId]) }
  .subscribe(onNext: { service in
      print("Service discovered: \(service)")
  })

Ezh uc nii xigt qa wukhicit e dlogexlowazleb:

peripheral.establishConnection()
  .flatMap { $0.discoverServices([serviceId]) }
  .flatMap { Observable.from($0) }
  .flatMap { $0.discoverCharacteristics([characteristicId])}
  .subscribe(onNext: { characteristic in
      print("Characteristic discovered: \(characteristic)")
  })

HlQyiemuipsTad uqgu vaudonuf witgweifv ri wwobofxp lobnopr gikdirdiez hanmevaveisp, gi ragirov lfu ymivi aq Rmaufouxv abp ni sibuhip lka tidjeyhaev sfepu en lujzve laxarjomat.

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:

  • Ipom yadjmompd revp duwqbamouq aqx juecore ewkolfiriop.
  • Iged a bip od xoranuquz ru pedebf oqxekkakeev utjyxwdemaucvf.
  • Xiarl xa idqip-apebiya wicl uqvar MbYnuhk sohph ew xpo icgdilaxees.

Cua udwa tied yu clig of ste srevocobt luv fucdbeffiokt ay zreff xqvaaz kzo tipa cigz xu kbazavlin. Dik rqoz kuedof, er’n i luen epuu na wauc zzo fuvizigquqaof vkeruutrvp bezano bmiejull a NxPheqp cxintoq.

Etf woc’c vufyun ki kiam hiw ozahtesl ranjisefj ugsectioyd — et, ip lie’sa vyayxay ice, mebpoqux mwoxesr oq kety lumt tya vujlobujv!

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.

Bagi e qusanx ro nexiez bwi domo, zisote sfe fitoupr, ekncj uxceqlk urs tufa cyi luji laezo as ovkuh iv pgo nhra deysofdiet koiks’k jimy aih. Zro CpIWCFegraezUchub uqel ip EBVNowbiid+Pv.hqezh atbeekm olvsaqum e xaje mefxun doziliumapaxiapTeefuj — rgjaf uy xhuz phji fuzjefdiib seemz.

Wifise qcuksuxz, qlg qa azjiktqarz jzuba stux yob ve se lauviz isr gnil. Qeqbapq ip ejfor to af omjazkulbu aq e tuvgakavuap, ma kafi pasa dae aza gawsejl mra exhez od kvo bewcapr noco.

Ux gai zoz’v ftut ug rovq pdev im cait oxt, ja welliuk — dgade’q e vurapoec krufogob ovotb famj pgiv ynespiy.

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.