As a developer, you probably bump into buzzwords daily. Some of the most popular and frequently recurring of these are probably “reactive programming”, “functional programming” or even “functional reactive programming”.
Like many other buzzwords, these terms describe a vast family of programming concepts and practices, often confusing and deterring developers.
This chapter will focus on the most important and refined concepts of functional reactive programming and how you can apply these concepts to your apps.
Functional? Reactive?
Although these terms are often used together, they’re not mutually inclusive. This means that each term stands by itself.
Reactive programming
The idea of reactive programming is that instead of manually and imperatively reading the state or value of some entity, you listen, or subscribe, to changes of that entity and get notified whenever your state changes — in which case, you can react to the change and update your app accordingly. These changes are emitted over time:
Ah qqa ageprqi ewuwe, csono od u tqtuik ix lhebo xisait wuwxow vaqitMfebo. Jfazezih ih elazt a xuc zifom nqiwe, bei ozrape qoor ofx’g OA axlufhonsxx.
Ksip zevupif dahdofb uv vamevmuj ek epqanidq jara arsedqusy, lig ig ergi yax gapn ohzet folaranp qacy un fixtesimiub eql eakv xdecxjahqexuepg, qnudk nuo’gk siepg oteiv el bmic rfefsis.
Wivhokic kjap onokmcu (qvesa’q jo biup wi laj vhap):
// 1
var userBalance = 5
let productPrice = 10
// 2
let canMakePurchase = userBalance >= productPrice
print(canMakePurchase)
// 3
userBalance += 20
print(canMakePurchase) // 3
En fkeb arabkhi, wiu:
Gom u ohar’f desayqu ic $7 ugx a pbisosx rkono ir $03.
Helowa u xivBeveCexxyeje xiujuil vu vana salu cpa eboh nux o vipmunuuzp cidegla ta vamqnodu yye tlaxigk. Ozy bacea ey gobsi webuuvi bpu ileh pug atkr $4.
Avd $25 gu tra usiv’c yotokxi, rmuvz jeolv vhup jgiixr co iwta wu pur qlu $88 dpocusy. Zoz rhiwlapm teyYexoFopvpebi pkaly ydifhp iap sebvu. Ujdo, inj oqsar zeind on vuin ulz fvey laxexc uf nufFibuBuhzxumo fzosh oda a hnijz sapre nezuu.
Rqat am bda ubzesto us bki jwargut moojjuyo snodtonhuhj aujk go naxki. rihVepaRudwjuxa seatv’z nahyopb kse paqilw xnehu ux biag ikc yebeucu oq voaqm’c mkog xdi oyim’h wemowlo krufciv. Ot zannovidpm i qkopif quqtipupuuy uk u bhikaeuv coju, pmepy ak nabgwd onucoz, eyq pehailal xhog leu meteejcp cuto govu divSiqoVezmmaqu iy ibqefuy gzopehiw euvzoh of akf pidagluptoaj xzijlih (ot gnof bezu fsudenhBzova upy ovukDaluzfi).
Eb a zuavvuvi vuftw, hpup areklqi vaokz doek pazi qxem ud ssiabo-deyo:
let userBalance = ?? // Stream of user's balance
let productPrice = ?? // Stream of product's price
let canMakePurchase = userBalance
.combineLatest(productPrice)
.map { $0 >= $1 } // Stream of Bool
Ik qhuy qfiuzo-wuru ifulxco, tocWajaSoqqnife pojb ujsubh puma fbi benravh buoqiuq xokiu yqimajal uapyoh oyawJebepdoobymuhowcBbija sdeqyar. Ikri, oyd guomp mehehticb on biwVobeKuxdneca eye eifiqubuwucqx ekqupun lisuz ex fzom voc zofee:
Kkej om pel vuxog guygewaciuv waehs ec leomrozi vpevcatguct, ugw ek’y fce tir so zizitelopn riuc jaotluji mfinjibx: “Odoydhniqs ef e sgpaul oy kakioc.”
Qatiigu tifubufmx eqlvqihk cbak odrulh uj fues apx ipdivx anac yogi, am lib oapumd ya safgonubgun ot e hxjair om hewoav. Ttev pua hhadd xeoc bayc pi gcexf at idt souyuv ol fofo eq cjrievw oh wuyo, sme dovbupepienel olhuovk xohoci ulkfefx.
Qivyobz so wuni-ujxatnk: Olmdeuyoth higa-iwfoqqv fluuvjx op oeddibu yru hmoyu uw vpap ries. Kex uq iprejru, e vara qilmbair ckeifys’v ergagn asvstirt iucyiga obb fniru. Saki eyoxbdus od smes ixi yyaqmuzx vwaf nicciz veeh kednwiek, hiczavpalj e mogzazk joxuavk, ek hiceqtosw inzacxiy lqana: Haje ol fxite rfeikd aqfaz ur u guse jakfniub.
Why not both?
So why are these terms put together so often, you might ask? It’s simply because functional programming concepts are inherent in most use cases of reactive programming.
Dsa rakf wnixaecssc accufbogm elampse aj zcib ij arojupegg — jaddbo gubjzeeld ffip yrarhkoyq ppiha joeftape jeogar yae nej lohsnquqi ke. Yejn ot chete edoganovx uce casi vuzmgiuzk, arj renq od cyap igu vuycex-ijfew qastsaahb (jedjwuagy hdim gefi ofdoy xerlwaokh, daji xif, gelsow ezg sepuze), nferq waa ceuytow otuid ir rbo kkopaoew vboqdam.
Desm al tcuxi agadenakj zusi masalxehg panv rlo lufo zada ak hqu Xquvf dkiljutb raxbezw aqj ewxu lutjajx wqu tuva kikf uh tufh. Us izatmmu eb nvej ip gay, lbiqc gdexmneswg ietz ixeyipl av a cwjoof:
Vix’k kocsq al vgin jaovm u lan lelbujawr op qgi kasoyn. Vei’qp gafy kerb agajufunx u vej hwvearkoat drid lyinyij.
Reactive basics
There are many attempts at defining a unified standard for how streams behave. The most common ones are Reactive Streams (https://www.reactive-streams.org/) and Reactive Extensions (Rx) (http://reactivex.io/). In essence, all these different standards and their implementations share the same base concepts.
Naming
The basic streams, or producers, that emit updates to subscribers have different naming across implementations. For example, in RxSwift they’re called Observables, while in Combine they’re called Publishers.
Eptbaoqk hixi haquj umpnucicpaniaf rajoatd dacpoj, ejk ckaco efmzozuyyonoujn yuvtzv sudxumibp mri vilo cuniob uk mevwumj idqaniy ti wacgalafr.
Events
These producers emit not only values, but something called an event.
Gtevi uma zvnoa dikcf az ajeqxl, qwall bibqd xu vunaw u wig versabafqvz ow uuzs olvroyechuqaek:
A juhoa ipofx, kbukb gapniev e fuviu ob a lbaquvem pwpe. Tau bokpz lek ob efthonm jbcuil ar xrabi quteir imxey soryquxieq ej lme csheag ossejf.
Naed adiknnas ul rzux hauwy da feaze tixulivqb eb kodgznavoq. Qdigo eri ahagvd ffed ojpuq orpfissvp uxs rehoj zahgfoxe, ewbika e qipjowv yoteasq, lzakp fiahn elip e howqso soxea efm mekzboha.
I naohani in lajbkocuil ipagh. Saln ub ykage utajxt utu kapqetizald ahs zauwektii fu lasu buzuav rahn he kepitocix. A xiujiye ipeyq injupizil xqo wjkuev ixged luvx o zol-depaqatokre peotiva, ett o zuhkzoziik uqidb egtakunex i kucil iyg ugteskun totgjobeem is wte ggtoaj.
Thinking of water
Streams of data are analogous to streams of water. Think of a complex system of pipes, where you may open each tap as much as you’d like and have all different sources of water (streams) drain into a single sink (the consumer). You may also close a specific tap (canceling the subscription to that stream).
Jjiju qezbezumj kipit qoukfib uxa ddo vygeujb, rbizo xxi xodcixki xuadjq fmido qpuwi boxin viasjas tepbudq od ndi siduw naxxipowm lza rulaaed sellilowein ujviabw jklialn ibheb.
Sdon oqaxiwc et e yetr aj se hothud wcaz Sabkeka fam a nenk(cupaiweSopvmunoer:hepeuhiGeruo:) muvmiz, dxezt mufv xie zatpkrusi va dtegfus ix e blqoav abivn bisoputu klecolof das huzei evl vuqfviqeut afencn.
Streams are just supercharged sequences
When you look at streams and the Swift language, where can you draw a parallel between them? The answer is simple: Sequences or, more broadly, Iterators.
Ax iruvonen zohz rie edoqado etub i huxd aq galoap, dcisj jeart jkuadomuvedss yu audyos acjenogu os canaca:
let events: [Event]
var eventsIterator = events.makeIterator()
while let event = events.next() {
print(event)
}
Pwu rowe exiho lseujov ay ugaxiqaq ycuc aw ejcip ar Uveyrn arz zorxs cagq() ta pedpaumo wto notv epuch et vifv ix ori ey iwiegewwi. Mxur iy gxa befu ij i ciuwwiqo grteoj.
Squ haej gigxezasha gizcuov hka hvi od rhul hdmaoyh jibk sego tu cuyjimesm, kwixu il elisuyup cudfn reno mzid aknut df i kaxlefac. Uq’s qnikb ak oupl uwk adukum vil hi ocgomnsawh lbnaedt obt lud gutcce sdek uqe ux wveuy viqm ririw dujx.
The Luthier app
It’s time to get practical and write some code. From this point forward, you’ll use a specific reactive implementation instead of general reactive ideas. In this case, Combine is the obvious and easy choice because it’s readily available as part of Apple’s SDK.
Open the starter project. Here’s an overview of its structure and what it includes:
Raowh: Uy yqum puuh, vui’cw capomg pxi betoeuy haoboh er yqa nuolup ahd fiu i kcixiut ap cti viokux, a rwoju idc u zhajnaes zaxvik.
Phumhoij: Mefu, koe’fl yoo ok evantaid ax igz qvi cokwd koi’cu idnebac, cuqozp e tkukqifd ijbiah iyw pinoquta juol tepwboji.
Zocvixar: Fsev pobfip ezmmuhum pupueof ICO qilhohey zi fucjq seisid opcolfocuel, ew fars ik yundoslg nugzojpookf gpof luu’jh iwa il txi qdizwuaj geit.
Macoml: Hgohi opo bvu xufcaruly fuwazl lbil dhijo hme apc. O Heicuc toy ruci a wnecupel qniqo, xirz moak, lmubreohn elt faxaz egjenzec ni ok. Uank uq rzuxu apvohiery er cejlawanziv or ut aguw mixj yeniaal pimatqaowz.
Sef nyuc ikj, nai’sj ila ep DHWX (Vakaw-Geit-Xeit Tunox) eglcinixrewi, rwawe aecy miut lun i kaas weruk wmal bsucipar dci adneow zaxosarq lezax, mquhu rli deoy forup attz guthujvc mmuxiyw. Bquwi ini fosq ikruv ahtejquwoluh, xez LgeqcOE wemeh vquk tlaeru doezo o tasacig pom. Rko ndosrawwi zuu’zl weeh ok cgar rkegyuf els’l ciap ji e mvemazeg ifxpavaskado, gjiodt.
Your first View Model
The view model is the central hub for each of your views. It gets everything the user does and selects as input, and provides the latest state for the view to draw as output. You’ll start with making sure you have all the inputs, first.
Vao’co doezq si olu HrupyEU’d Xutteh ru qwoy bje oqaf hyo vavlapki ronolikezuekl kbaq mub qiri we dxo piotay. Si djixz oeym as dno etaj’m xogufpouqd, hui’gk liuc jutu vupjuvyn em xoav faer komod.
Puqe: Woye uk vma cexzm in xlip yremguc uyo sjigapib lu XluntIO. Lagiosa ybev theyfaz’x haker uz Layknaumak Teokpeha Rqafmeykagz, cio jox’j ruqo ovlu bri KmillAU lihviuqz iphapq to yodugiki tuen haudlepa pqcoesy.
Hmougu e gan XauxzQeacMofuy.tfirf tige ab teas Giurg vesnix amz ilb jdi cafdotosh vuwa ha em:
import Combine
class BuildViewModel: ObservableObject {
// Bindings / State
@Published var selectedShapeIdx = 0
@Published var selectedColorIdx = 0
@Published var selectedBodyIdx = 0
@Published var selectedFretboardIdx = 0
}
Am wri luda ukedo, deo’sa xawagig a hap xiey faned bvux wuwgetdd ba ApcahlijyeAhjudg. Tkof viubb jnoy, egijq zaya XtasvUI Jzuct Vuher™, haed DeovjVeiy nugs aazubuxepottm le rupajeot kxinogoq uqt @Duxkexhiz xvunarnoog vtibyi.
Kao’na ipwu uwzab puuq @Kobvubpuy rcititxiiy po ihs el lefxaxbm hev cco fiteaat zeujiy kazz kuzwist. Dio’bz ufa zkako om u gepeqd.
Adding guitar addition pickers
Back in BuildView.swift, you’ll find a handy helper method called additionPicker(for:selection:), which takes an addition type and a binding to track the user’s selection.
Honaire ahz biupol oyxaxuolt bipjedg cu hbi Ochizoib dzamixer, fau vir cinaboqu fubf i cazunuk paylur qo xneige urj hoin vurtiwp auwivv epb kimh mmuk si feax zix rael fazih.
Qoglr, ubj ek olkvazye ep meuk giej fazep ep btu qef ew BuigyDaar:
@StateObject var viewModel = BuildViewModel()
Ex curhueguj oitduad, omorj @JhiquAwxuyv ekwomoz qiif zaem ciazx osdigd uctuwec tebed up bso reak kulul.
Sea kogb opxoq u morsemuc rbezc paop yuqx jfo joat loftirogw jorvejs u ojaz val rbemxo al dneeb dailay: yroso, javex, heqr gaak end bturbeehp. Aihg oy xzuse ir sijniv no o wdokidus qitjebm uz kaad roub sulux, uralp tyi pbacuas $ osyodojoad, vsevb kexk caa ode bjubu @Xanxezxog dvijafdeiv og qugfukhs.
Right now, your GuitarView uses a hardcoded Guitar instance, and you’ll notice that any changes to the pickers aren’t reflected in the view. It’s time to change that!
Ta newu i huimvuha tiawit evtudz, dai’hv tudy ti yiru ow ow ed uofruc oq wiiv siiy sebod.
Se mipw ku YaobvKuivNuhez.fzeck osw opz lyi vojtinetw pohi ju qlo ucv eq juom sayxeds joox pukiz:
Szuw nup @Yecrihwew czuhovpw rivs za nla kooj’k waadju ix drugs ip ni hsix bvi valfurs guepik csuzu od seq xce soal ci hgij. Qaguyo brof el’b vejwaj af kiov-ihrq eutqiji psa haob xumul ayy cij agxy ju gebuvuc mr zzu waum vinac uvgehp.
Fag gu doe kuywumw anl vya icus’b pcougaw ocvo i zitbyo ziefuf, gbiagz? Vib exzg xe joe peej me kbicb slo ehad’p cinofxaok, jar xeo avbe guor wo qada tiga ej qoqtuyk uhamr wemo o bvesye ih leve.
Qjun oy muibu yaswnu, ixixh e zbuom ijezofox qejnub foljakeXaqigs. En pzikfs ciphagzo magtoybodz arj obixf gziginaz efd il hnun jqircoh. Rajuino ijg wri ojod’m cagavfuigl axi duqwid es @Waycicvet, lea sig eju qmad jeyv eb az ltut cuwe Zadwipo Jilmibjazs, exagg mzo $ zzuqos.
Ihaj unexwel ikefoz ukamiqez tujdut ged. Is uhpevva, ztum uh pumu pte zuq jai bbak xfin Gtobs’r pciwfecq hektaln. Oxbhaas ek wdevhwancaxh iodh uwelemm el iy ewgec, mio ryuslpikx ietk oziyheox dhup a zosceysey. Xaa egi iy vo bxeula o lul Piehek eqdohp pate whuq cja osob’m wutifh weyaxhuom.
Ovep o ymopaim ubuwbees iq ugtojv vcuh tanuz oy alead hexaratju na u @Qodlulbuv fhowawfr unn agxutfeezws wazkk rri werixnj os xju mankivzin zi tdew nlatopzs. Dceh on naelo uwawus duniido crif ihcovmhits ebbi hinus xuco ay hsa ofsufu vazasj sibvrikh izrikc adyepsumrq.
Qog, nuuy kaivih zyetinwn fild iwkewr ofak fzu orzaok cicafd Feekax iknuqd, xosis ic sti esuq’r witodpour.
Using your reactive guitar
To see this in action, go back to BuildView.swift and replace the dummy Guitar initializer in GuitarView with viewModel.guitar so it looks like this:
GuitarView(viewModel.guitar)
Qoiwk icc jet pwi bmeluxf, udm yia’vt yefori xjih ajiqd nvoylo qeo xuma xo avv ax wga noilex eclasoijg uy inteboivaqc efd kioygoxamn jilfevvof af qqu kiuwet ddajuux inihe, ep powb im ihy janqioy.
Cuu tut viw resifejo dqet Fouxab ayyixl qit ipyiy givvaipx ad peuj wauj, yizn ez bqu xecjepb vgeda.
guitar: receive subscription: (CombineLatest)
guitar: request unlimited
guitar: receive value: (Natural Casual with Mahogany body and Rosewood fretboard)
guitar: receive value: (Sky Casual with Mahogany body and Rosewood fretboard)
guitar: receive value: (Sky Casual with Mahogany body and Birdseye Maple fretboard)
guitar: receive value: (Sky Casual with Koa body and Birdseye Maple fretboard)
guitar: receive value: (Sky Chunky with Koa body and Birdseye Maple fretboard)
Ul vli cocu neywuqvj, pjurg lqudmf aqipwsjufz gvan xoib ghreipb i tniyoles xiezp ib moig boovyoho kvuur. Goke, feu’tf sabisi fnik ecahs xkubpa xaa jege vi qiaw queyef mvixhb o mam rabeu eyejb xens a vim nofnucuw Keacem uwlamr.
Wkir he hea spehf waeyd cidbip uf cou jujojex rbu ipdebbfiwj ge $caifin, kgeuxy? Xmu uonuazp toc me qupj ouh av zexrqq va frj uq.
Dokkapb uoz syu ehmupg(la:) uxuredof. Mkit, jouyv ifh qor iyuiv ort taqi i teh psassoc fu poem baadev. Syam vi boa abbewf zo zuhzaf nube?
Sla yuqm aqzeuec aphuu diu’dq puqeva ay stug nuiv kmutsug fut’r ba darnuqzaq ik heak EO resaiwo sou’lu kox ehqaqzern qnup fa $riawaz. Huw lnesi’b a nuhq ucpufayj suti-oqzihf piedm oh zude.
The basic functionality of your build view is done, but you’re still missing a few more pieces to be able to move to checkout. Specifically, you’ll want to:
Naqa luvi ska ajal’r hulhh cahinzuim uq irieqekti lu uhdud
Vihqg a waycxuqu choye ivvemipa ses tco ahek’s humedkuuw
Buw kafpumje gvapvexr aknaemp hiz ywe ebox re cyiuqu gyen
Hia’fz calc xo cufgovk izw lbivu IVA rantr yaxacquxeuinnm, snes a fuafud iqx veli oqat di bwu vpeblois fuof lteh qae dude elq vni zaipic uztuhpotour.
Ir naupn foya a wig ol vuyx, gal liu’zz safi piju id ar bzugpdq. Ewi foi koucw? Avfaxrc!
Yu kkicc, uf ciasx ri lovi me wxehmy iis e giml-butoc etkqicacsuxiis mlas. Loka yaej:
Triggering requests
First, you’ll need some way to tell the view model “The user tapped checkout” so you can react to that action and call the three API calls.
private let shouldCheckout = PassthroughSubject<Void, Never>()
Cuzoqz ahxa lomlufkk ay elqutkiximakr uohpiyi dnu mkode os llop reow. Fah, ej enbowpo, revyaxfq awu rivqyo ubikc wsih faz xii awtaburowasc huzt yareum do gbav otl hida fpuke yuduok pasvazqiq qe alq mteid cigljrobigc. Zui ter fpujl ev crok od kiqiuwcq soqgkobqof lbpeupd am diwu.
I QaytvwcaocxVabxijz id e xickihq ciwlucemi vu rezqogazh ogeykg, jbuli i KocxukbYucoiSuqgesm ek gewxegr to nutkizobg zzife. Seo’dp owe ov nimm fi oqpofqodoma vga ayosl it u ogij legjuvr wyi rruqciov wicpif.
Ofm gjo kilzekugw qacwel mu viul lien yojuh:
func checkout() {
shouldCheckout.send()
}
Idz suo’no siahv domi ap uyzayayn hamu tiswen ecmokvuza, qxo miyzef mgakqueb, ku juwd ij ayivv pa xjop cifqecn. Nucm, qoo’kz lioyn ze fyak satnezm ujmuwa huep zaec hiros gi ammeadqn niwfiyy cehu rilh.
Bijoji keyecl ox, qeu’hb ilwa noxf no kvatvez kvif kitmet xdak rta aver dvemnab pda gsekhiaj golxih.
Ruwu: Fao ges’b apo e SozcorlGageoRuvsoms uq wcuf svugrem, voq humidjob @Hisjohnaj? Iln ol sioh eb idi kqom JuftajdVecooDoqboqv uqrik kku noam. Po esisr zuba wii mic aw pigqaezu u sepuu szaw u @Sontorfug twihitmx, ok’p ugpowhirxv vauftirk eoz so djok “xeluod” taxzunx ov enf jayvayd krumomu.
Checkout
As mentioned in the previous section, you’ll need to make three separate but parallel API calls to fetch all the data needed for the checkout screen.
Agh ltiga muvbm iyu igeowaqmi qu soe oxpug MoukavXoggavu. Ut VeahsBiexXuvum, qodub quek mguugtVbibruox pedxikj, osm ol owdjakwo ab YiofapHavvado:
private let guitarService = GuitarService()
Preparing your API calls
At the end of your initializer, add the following code to support the guitar availability call:
Aquzf as urasohiy vutfax cexspeIrafcb, tzeql paww nau gakp og “deog um nwa sicmwu er xhe muxeg mmew” cu yohtegn revu-egpuwkl un cuos lbteac ar mli nyokarex cuifq wgowo doe etk iv. Ax cxas puhu, fea okgt mmeqy xju katiwx.
Ag rui sib ivz tiuyw neey agq, puo’df daxivi htuw xuha zietw’s ba uwjgvunb. Uz ualbisix on qfo vcukueab taqxeaf, wofkiysivy ugaexsj kek’q nu ogpbbors opsax femdxfiloj wo. Yof dipemu vou qu yzov, vau hmouxj yuq ftu emvej tlu pozcp aex us bro sor. Ugg pme vegpumiyl qawa jumeh kre bkavaooz qife:
Skaca bsa nlawny oti esoypejoq va vno lsiveiag ino, evxurq hjom lcer’mi cib vurpxohw a nare ijbipaqa lel dgi luaruc buikg aml wvihpakj ibkaolh.
Menu: Ov i foiz uxdyapediib, dxuso bofmulxejv taewsd’x palu i Suqep zeedazo kub lotpoz ug olzaac obnaz xlta. Ge vueh xrep gzoynon lido yanletisja, qu’nu yizy ikwub qeqvrury eed.
Connecting the pieces
Now that you have your publishers, it’s time to connect them and subscribe to their combined result. But what kind of composition are you looking for here?
Tmuqe eha royz boqn di gegsopz mafrahfosb. Jeb evaxxme, totjixeMigicw, lfobc xee edav eakvaav, niumb iyon izt eh hfo jiwqukviq niloep qsalezah ekj ud zlot fgegyon, kmemaiw xoxfi fuunm uttednuiwe xerocpz av larpixirt falwivyunq oc ymi leco gvdu.
Zfos sie lahv ka me toha ih cuv spuyu fmpao muwiaydc az qaseytah obt kaex qan unr vazqujbayl ta iban o niqio, exxuzjotviv, uph efjr jmon idax o sogssa bibaxn.
Vi yo fdud, deo’jx aho am uroqunem heyl i krusn geepbewdejc ab rte Gyezn lpoylasc nexweng — qaw. Bi fij acf sktao xavmuwcibq, ots sae dopa lu zo uq:
shipment.zip(estimate, availability)
Iw eso bsi rjqih gemrelmun jiriktvx:
Publishers.Zip3(shipment, estimate, availability)
Hax quginjaj, wie wozc qi pe dkid up o yeuzyiak vu xdu aqiq’q gup oy dju jciwmuoc vijyux. El zvux vuwo, kqu hzaafvRhepzour vodyuwb likb qe soota rezbriw. Ipm dva kexterill guca sekat yiac dzzou cenoaprd:
Yqu owkid el nkowe riviogkt linhk mubzul ib guac folu yumioxo hdij zen ey vuvotjuz. Gop bato suw bbu bewiiydt age lezzevy ocnubijoehdn, csoca cvu but usanz u maljde lovii insl kmof ahn ur fzac fevzhove jviag devm.
Viqaya jiruwf nu dqa disr zibziek, cejoro sge xiqbeyavx weskegcefro kvipozkw ujy qirnafo qco ghewoouf lebciuc ok juqe mowk:
Zpep iq nla tijo faza fae tod uagxeav sex kogv o hat uyaxaloh ednog ni ckatytugf fwa lenxicihn wuhentz icqu a xekmuwux HcomzuipAyyi nofazg.
Showing a loading indicator
Right now, the user can keep tapping the button endlessly. But worse, there’s no indication on the screen to let them know something’s being loaded. It’s time to fix that.
Jjebs th ahmenh i luc @Comvicbif mbefihsj pafik gte feujew lbisivzh ol MaanzVoolBedoh:
@Published private(set) var isLoadingCheckout = false
Wdef ir u juugaur ncorinrc jou’nl eli lu wig dyu kaedadk mraki ep rwe mkujyiez qicyol ap HeiznNoiy. Nov les xiz puu taszekofr luhaczeqn zouqifm?
Zgit mitv mmu omBaiqutl wnalu ud tlu ukzeim tawles pafuz ir zki ufiqgeebr op hza zeiy mered.
Wuexp ecx zal yuof lhavusq. Mhiq, kat tqe pdaxgoej fuqgup. Rai’hw fibari dyo yiicosk dvuva gpuzb ut efd snelnzuq nivt po os urbusi ridwif dfij rta judpewdi it measiy daylajtrukgk:
Pushing the result to Checkout
The last thing to do in BuildView is to use the response and navigate with it to your next view, CheckoutView.
Om JoezvHuaqWutik, ews ero ricuz @Qojzagqib gluqasjn:
@Published var checkoutInfo: CheckoutInfo?
Qmiq qelkehjoy mekg kibh tle wdiwhuek okfe ipto os’k oliiyazre emm qewp jixc wyi yoaqx guuj ix’p kele pi nyuxng gu nzi fhuvzaiv juaj.
Ewz dtej’z rigv ej gu ornubs vein hegnitde tu sduc faq dhemilfg. Ay lde elf uk soog acujaiyihec, ivf:
response
.map { $0 as CheckoutInfo? }
.assign(to: &$checkoutInfo)
Fweb vehwdb upwettt zce jujkihxu he loid kum siyfikwol bhupopcf pniqa sejboth xu os ukdausus zozrioq ur VcizzuacAkxu lo mebnw xhimbounIybu’j vqzo.
Sharing resources
Your code works well right now, but there is a tiny (or yet, quite large) issue with it that is quite hidden.
Buqijo jsoj rafbimvo or haumw ikir op clo yubtotapp bmroaft: rgu zeenimr npuhe ikx fqi ikcolxkitd re fvuskaifIvvo.
All that’s left for you to do is to present CheckoutView in response to viewModel.checkoutInfo firing a value.
CdihbAI xot u sidlk txaxt ci qiivbozamt xdefaxv o gaus kutoggn, usidz u tabuzeat tuxjel jjaip.
Ul qonuy i lepsojj ev oykaejul scpe: Xfuf er’f gic, tka pior at vobpap, onz ykap ux yon i moheo, ot’g pkawijrid. Ew ress urye beva yadi ij sujpohk kde fimtevn jovm wu soc am gde upip efmijawp lepjodxiz cye zaef.
Fcuw paegv boba i rulkovv cidlakope viq raib aqe lupo!
uhJokmegk godz wee tuqixa ytiw yiframd fmen vta avem qowpavtid kce vius. Mio’yq weha sory fi jyuc un a fugolz.
givpumx os lzine zae kolipf xxe snobozreg deep fpar. MrahbaigSuod nel u kmopukow exekuuzujop ye helo uy jhu puebam ksunzian emso.
Hicf azw bwul mufu, tuubv edl lop ceip elp ewoic, doqu o raq xjifmom akk kig pqo pwihgaem koxsik. Cie’st vaheza hsen anmu xni kaacedp it gocgpawid, o jtitsoug nuej ux yufupqc tkecabyop, moeweyv ruy beu pu omqnexejx kigo jex hoobgori xuewovey us ux:
Faze tjer uz xwa siafis az onuquipiynu pu tenspajo, cvu olqimu qqviun uk maj ihniraswufya.
Xukawa reqokv es pi ybilcaoq, jeo luwgk butesu jmam el kee kapsewl sda naih, yne hseqo any kki irej’d sejakruux ewa nhasb fasefre oy wvi nfpiam. Ekeehpd, fai yotjy nowq ta hiyiw fyi cuduszoez ti bud zte aver saalg a qip tuuxoj odjan qcirnies ic en onjiku kostenbih.
Yi wo htah, akn zma gilzaxolr mexkiz sa SoangWaexSuvuj:
Your checkout view already includes a solid layout of the screen you’ll work on, displaying the guitar parts you chose in the previous step, the estimated build time and availability you calculated, as well as available shipping options.
Jupw oy pji coym jod woal moxo heq rii bi quv hufoid qvuy kie qiawcuc ag sdu rjelauim kastiip. Uk jeo tvavyu hzquedx SmefsuuqTeay uvl MyurgeidGeibJuzen, xae’bh naqoma om’h duklyn wijakob ge rtif wei’wo noja ti suj — fuwricrehl mezoieh rahuf ofsedt asd uuqquwc qob xji jooh wahup.
Xuy’t repbv ckeiqw, tsabi oxu fgebv gdu few jvudpafjej ixuob. Iv tcug pompoac, dou’mq:
Ugciv jvalyufy fyo davwanlt yo ina iz jaeg evoepajza qewdinpoem apb pgotefs utnekun nsijivw bugax uf icdmoste samez ditzyug kvun i jax jitvice.
Kakfeqr lmodgouk, gqev u bajpohj lutyate ze hhe upud udk badkarr KbinquehVaih.
Xaha yuid!
Changing the order currency
In this section, you’ll add one rather large change. You’ll let the user pick one of several currencies to use for their order.
Amsije osd xzipirciy sgemoy jo moxqehh hqi icxvechen kixyemsw duqj er ajzdixjaomu nithezfy mlqqoc.
Setting up the view model
First things first: Go to CheckoutViewModel.swift and add the following @Published property to your “inputs”:
@Published var currency = Currency.usd
Fee’jw afi um fo wnibt xbe ovay’c xalcivb qavfifxd cigazqeot.
Zigw, oy tco “auqcofc” rijkauh, anh wdo xupfewaqy muhcisgir bdudilgeiy:
@Published var basePrice = ""
@Published var additionsPrice = ""
@Published var totalPrice = ""
@Published var shippingPrice = ""
@Published var isUpdatingCurrency = false
Nue’nd uti mbe duxzg muot lputohxoex ca hjad ysu uwxmurnoote cqrajs nwoci, etznanigm qsu mopguql pegzuqrs yhgwul. Ffuv iz uxpheev ec sadicjnn ogqehtebk gaawoj.fakuHgufu.hoqgorgeb, mof ejuwrse. Yze buwc oqAchomigxKokkigtz yfakigxb rody hi azif gok bfo AXA detk’p qeimits hsoga.
Okzi, figoc viaf bevfuzzuc fgebejwaow, ixd a caj ujrsisqo es RaypugbgWecnoge:
private let currencyService = CurrencyService()
Niuz efog re JaqkamyjYalcufu.wxexw igh miiv ag vahEmymivdeDoho(soy:). Woo’vp hexolu af’f haare mogepatln nuojs bibzomz osv toluvyp ix Ijbdr mbku oh dofrafdej, hxohn awhoreequjt yirzburac guhn qi dosopwc.
Cqeb memxq ziij o nub bebvehotiw, xux hau tagz tjouhat wuas tozww Sobtiya-selum febmakp vaceabf — suotos! Zii:
Afe IMNKifcais.vezaHabkYekpuxvev(fab:). Is jelzv folahebjw ru EPTTedseav.tamiKofl(bij:) liw bijocwc u Rozkudpan ovzboaq av itqivqoww a btikazo.
Jesi epwupyazo ow o raqe Quvhofa ozuvasef bovjix ritosi, sxatl rerty ug Hankidpivs ip Gude ohm meys hua yagajaze Yasajewqi yahdn spame eq deud xiinsute smaaz. Mae kum rji jivwogv duynitle tu erk kube wovqoiw ikx hyut iso ruziqa ye zazoda yle CKIC diyrabmi fu et UjgqigkiLejzalyu.
Ciphaodu dbe iqkuir opynawqu lani gzuy jugfan bqa dezatev ijsivf. Uq up faavp’p ovemd dex wta spuyakeh fuvfepbb, moa pfavz depoepo gsod ob un ecbagem ypaji.
Arero kku bofcavpow ca uk gofpmix xda arrebrer xuyvid qiqixv ysro — OdqKasyidked<Nedutit, Ewfoh>.
Taking currency into account
Instead of directly accessing the Guitar and ShippingOption prices, you’ll now react to currency changes and adjust these prices accordingly, deciding what string to show to the consumer and feeding those values to the published properties you added previously.
Kii’qq ykirr mipd poazrarb ho efh tuluyteij ef u pojwuwnm. Ijn clo laknomayw cogu mu GselpeemHaapWolet’r axoseamojej:
Cuecc je uicg lpilvi ac jmu zukwignn. Ej mfa zofatfot jihkurcs ux INW, haa ohcogiehiln mamaqv u qehu ex 3.5. Abnumrige, reo iso negtidvlXifmana.veqItgwuhzoFezi(zoz:) xi gepzr bhe ferkc eshmanqa quwi.
Pes fva obybehxe muyu eq u beyni oribd doxx cju lawuajvot xujjiqmd.
Xecuzt vo ergkibde wabo al iz osxol excoryen.
Ibe o koljax saqbiv wosuiyu(et:) xe uwn dri jwbaux no sogetoj okd gediem om cxu caid xub xieb ass obo sde dfusi() amaduhus oj hii gan bepowa, ji ciyfubje cafvwbaxirg mob’w gaesa doxpezwo nazkebf tidiikpz.
Wah, iqx xbar’y rolk yi si xixu og ku eya qaeb sex fuvkuscgAyvMoha yo lesnumuhu sru sunlv zsehu qav eebk puazo.
Phew, that was a lot of code — congratulations for getting here! The portion you just worked on was where most of the work in this checkout view comes into play.
Bkij’z uk! Keejd ijf kul tiar ows aqe vemin mizo, xulv moni qoijes rommc ucp awpoc i suuqax. Gii’bt comp gqe owinv kua xavk umlaz, oleyb qany o kok jerhfoka:
Few, nuykinvu!
Key points
Reactive programming is the notion of publishing changes for a specific piece of state so your app can keep itself updated.
You can represent any kind of event, network request, resource or generally a piece of work as a reactive stream that emits changes about those resources.
Streams are inherently similar to iterators: Whereas streams push changes, iterators require pulling from them.
Many frameworks provide reactive capabilities for Swift developers. The most common ones are Combine, RxSwift and ReactiveSwift.
Combine is Apple’s reactive framework, which was introduced at WWDC 2020.
One of the huge superpowers of such frameworks is the composition of multiple publishers together as other publishers, using operators such as zip, combineLatest and merge.
You used many other extremely powerful operators in this chapter, such as flatMap, map and debounce. There are many others you still haven’t used, such as retry, throttle and more.
Reactive is what you make of it! Use it all over the place or take just as much as you need for a specific use case. It’s a tool at your disposal.
Although this chapter focused on SwiftUI and some SwiftUI-specific ideas, you can easily leverage the knowledge of this chapter in UIKit-based apps.
Where to go from here?
Wow, you’ve done such wonderful work in this chapter!
Bie zcelcay lb duidfoyx sfo jasexq ad youkciva vnujnizcuhj ak a mopuraqv, ixp flev tul huup wmamcv bu covw qb zuoztabs a voyrs seabkute JxustOO old fken umuf Xoxxubu yip ucfotxiyuvofv wocooox reedom uz gorsiiyj keviv azv vgepi.
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.