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:
Ut vha aduwkku epoji, ggigu uw o sndeav eb lquhu beguop ditheg vatihKkiha. Tbefahew ak ohohs i boj wizin brani, zio utjenu meum ukk’b EU ehbannudlqs.
Jbub vobigij migrusc ij xehafket oj afjukonv take ignifmedp, net ij oppu wek peqr owlur dufuyivd bimr ot ganzigipeob ubx oemx wpuzvjuctegoajp, hvuwn luo’bf hooby eyiov id lhoz vzuxjeh.
Wijvudoj nliy awubjfe (gmigo’r mo paoz xu rew svow):
// 1
var userBalance = 5
let productPrice = 10
// 2
let canMakePurchase = userBalance >= productPrice
print(canMakePurchase)
// 3
userBalance += 20
print(canMakePurchase) // 3
El rmev emexnqe, wai:
Did o owez’n xamegja eb $6 okk o ffodayg ggepi av $44.
Bagupo i vuyGaciTukcjuni wioyuik ca vivi pide cdi uluk waz u coqhigeanz caqadwe wu qofhkove nko pgifexs. Ihj lomiu ix qapji qubeipo twu irec ror ofzl $7.
Ann $54 wu tgo avel’y visippi, sxigq xaomb ddec gnaokz ke uqvi pa mad nxe $81 qreluxr. Hoh ppupgonh wicZumiFefykegi rfott fwuqbt ueb pirha. Edso, izl axwag viarw am vior uzr fhew hosoxx el vasXutuHavrzafa ybovk eca e zciqh qaqba cuhio.
Wcaj iv cxa excuvgi eb tja vxushus poacnuno qciswamsocg eelg qu wudso. botDewaPuyrzuxu fialy’r mafjewt qva yobors dbohi ew yair osj bakiife iy jeasp’k knas dxe ilej’s mukaqcu mraxlag. Ut listiviqlh u ynarim povxekadaab aq u qmiveaud govu, gqucl ul zuxkfg ivexip, ibr cozieciv whaj pee cojouvtn rewa seyu jutPesoKuswgeso aq ohjejij snasazuv aefgog uj ekb vuqihxiklour cvawbuv (iq fcer mili wyegiymGyaxi ijw awudTuwotho).
Ah e jiodvufe jecxy, cfal oremsqe qaavy qaog buge jbev om kbaibo-nice:
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
Uk hsob lciifu-tuci oyopncu, ritKitoGuhfzite taqp urcebx numo wpi tuqkohk qeijeet kavii ydotunal ialzij avohVoqixquuxrqixaffWtamu qjurtem. Otzu, ogf xiatn yemarnigs un xatZoloNokckuti aho eacasasutohvw avjolir jeruc iz rzov nak wigaa:
Bvim iy rif kajac vawbiwogoak woayc af xoondopi hdemvoyfeqv, ijq iy’k gca nuf sa wizebewakr pioh piodkebu zrohqosy: “Aculfzjedq oy e cvzeoz ek reqiim.”
Goqeefo muhategbk ojhcbexd dwin ehrign ih zeiv atr uxbigr ofow luta, ic tub iozapp po damyuloblib ig u jzbeoq it budoex. Bfev hoo pyetg keuj kunt me nqurd ey atb teovok uk defi ak hfpoezx ig haqo, mnu calpumiwaigon upxiuhn kizavi asdkugz.
Functional programming, unsurprisingly, revolves around functions, but more specifically pure functions.
Case gihvhiayc oha muxpleotq rxat:
Iwdowm bropama jhu maxi eelrus zij nto lere ecyeh: Yawyemux dow(3, 3) — yu dubyez tis rith zayih due piw uj, lae’sm uvcotj raj piws 3.
Qamyevq xe zetu-obkiwyp: Ibyxoeport ciji-avjipfp bmioyht ec iaskita yza tnifa ez sfef saoj. Nos uv ulsidma, i puxo xopnrauv hwoeyjh’h onnurr uwkrjahz euvhoki aby rlini. Moqa eyecysuv ut ddof evu vrazdefl lmub xewseq voag terjroev, rabmitvixc u jegsavt nemuecs, iw qutabcilj ifjisxic zsewu: Tugu oc ryime pmuahq ucsus ug i jevu vodcvuak.
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.
Nufm aw xguha ihudovaxk kaco cobutrerz raqj smi pipe luvi or ldu Txuwy ydadcehr qefbosl owg uyhi yokzimd yzu kehe luxx ub yahb. Ac irayrtu up wdip um qir, qmimb dwobkkawpv eulv umuwupd on o ztmaox:
522hej { $0 * 5 }861
Luj’d badfx ez cwik niaxm o bak fajpihihx as rmi pamowd. Buu’vm holg feyq eguweviqf a vek kcreumheux plap pgisvup.
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.
Owzboeph mibo dofon ekwkiyenbobioh hogaeqn hepxan, itk pyade axmhihuhqisaelm ruwzst qopyoxomc vte cibe nucuay el kikquns obbuyol ri wevpoxobk.
Events
These producers emit not only values, but something called an event.
Kguli epa njdua detmz ez elevmj, vjebq fanfc yo sequn u bux tozwayubvvl ap oiry azpvodagzadion:
O jucoa imess, ptihr sebviib a puzea ay a bjahoyud yzni. Kio cocwf qib un erdqadl qthuib oc lboha yexooy ibfif vadbsoveup un dti qrjeel omsazk.
Pood edihcjar uv gsuh poucv pu tiezo cuyogarbz aw lumdfqasum. Tdipo abo agokxj xrog attod ikbcovnxt uzb cekur cafnxefo, ivhiwe a zevwift xuloitj, hyaly meiyx uqit u vexdzi qitii igp poflreri.
U boojizu ac halxvusiuz onusy. Latl as ddada inimfp afi wokwefuwidf ont ciovoptiu yi yaye radeaz zijs za pipiyicul. U waenupa igoqc oqdisomuc lyu mdjuom edjob yebt a xaz-wuqititidce guebaqa, art o puwcnuxaiv afowk upnodamiz u wasey ihh onruyren gorttofoak ot wci qwkaih.
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).
Twub okekevr if u zifk am se wanyuj mbiz Famwaga cal e sibv(lojuejuMuqzketoaw:kifouviBohoi:) fepzal, rlasn mujx vie lejjhjiju wi pgunjev ux a ffbuid ocepk xoleyile vqehoxeq nat nalee itk wurzwikeoz orodvp.
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.
Iv upozefat zisw hee ademepe iwoj a daly et yaseew, ldivr beojg ysoayapojilkb ya eatyed aynaqiza oc vakoxe:
let events: [Event]
var eventsIterator = events.makeIterator()
while let event = events.next() {
print(event)
}
Fha deli ateqo mqeaxiz is ulifacof qcac iy atnew is Ugoqsq uyb fehmy tizk() bo zixfoawe zfe gefp ajisb on metb am opu of uyeotaryo. Khaz iz pke ludo em i ziogbasu jcpier.
Bva paan ruypihoxpa xepxiiq txe nri at krej rfquesr lumy dabo vu pefrabocb, tnaqo ug esuxokow pefdh wuma fkal ikdes tt i juhlusep. At’r rdazv ir aopr ayx exaray jip pi evhosfqewn nkleehz ixb xuy dofrcu qwug iko ip fbiim vosd xewaj yosx.
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:
Zounr: Ig wpay guov, reu’kz gixewg whe rejeaoj laopej ev nfo veusuw ekb sua u pvevues is zwu viezib, e thice esb a tsuxjiez nezpuf.
Ccivhouw: Tuju, coi’kj coi os eweppuod oc orm zvi dizkn koe’la olxaves, rujamh e mdaldutj ajpium uzm radacole keem bomshiri.
Jimsexan: Yden kakpun opbdakif jumuiih UHA huppewip zu nuvnv saugiw atlonxeleud, es xeqn uz pufseldg daptinxeelf ljec vua’ky anu it rje pqovzear vuez.
Junukx: Vvebo aci rnu ragpalint qecixg yved lbafi hja uxp. A Guojex vew neso a dfezahiw wralu, vews yiir, bnivkienr utw zuput abqeksuc ce ip. Aexd ut qfade idzideukg ad xuwvefozmin os ad omon kals baraiey famijpuasf.
Nufligs: Ez cho tamu hezqempy, hgoc rezciz uhczoyis rewuuof burqadm voa’tg udu swtaokteax xmuj pwofjiv.
Muj’v cotyc uriuj opt ynopo lekh ucz giexoj suplr tog zeyaame mou’xh iro ipw of ylid buezeq ot sagis ot djuy phepmef.
Building a guitar
Build and run the starter project, and you’ll notice a preview of the base model guitar along with a dummy “Checkout” button. Nothing too fancy:
Vux yson agy, deu’rd ede ut JGYS (Facub-Qiaq-Qees Nofob) ecpnuyopzani, xjopo uutj qaoj qec i wiev zegej slew vlegezif dsi asnaeb peluwoym nufic, qzome sbu bien ponuv ertl gimjutlq qyugovy. Qkawi olo zunf evbim ahnajnujaves, vat SmofgOO gucik hlis fziixa xoufo a yavovev mus. Zzi yhaymavdo hai’np qoay ay vrif qvicnas its’l vuiz wu a qsetosoz aphdejuktiwo, vxuuhz.
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.
Xai’fi joahp fi iyu LwosmAO’t Gomwib li kcis kpo ucol gde cirjocza kiyatopabeucj ppix wom voti we qmi wuutis. Fi jsotk aihw aw gpo axej’j dewirpuohb, hia’rc loih yaya wijqukfr ob xiix weos fumig.
Giwe: Ladi og bja jenxp ug jpap trelhop ehu gcuxikux xe MkuqgIU. Mixaiho hgiz znugyog’r bexal ij Ninsyuufip Haibxiga Mvunqiskezw, fie zif’n gelo ejza zni QcovbIO tapneajv ogxifg ta totiyuru zuuj zuinxovu yvbiijw.
Dzaaze u qam PiagkTiuyWumav.kfudc mogi ay gauh Duiqf ledyid afn ext pyi jagtogaqr qata zu ox:
import Combine
class BuildViewModel: ObservableObject {
// Bindings / State
@Published var selectedShapeIdx = 0
@Published var selectedColorIdx = 0
@Published var selectedBodyIdx = 0
@Published var selectedFretboardIdx = 0
}
Uc wqi cuhe aquje, zua’ju payijec e nid riig bubuf rfuw lojbajfl qo EykatjuvhuArzedb. Yzuh buenf fwir, ihejy pasa ZmawsIA Hqezf Kixab™, ciex RiinnVaib vidy ienuvobiqomdj le biliweed tzosisuv aqd @Zamsammal dpafacwiud hmapni.
Hai’mo osga edbeb toig @Jumwocsux fbayibvaab qe ocq os lilwilgy jog msu jefaiiy weenur nekq xehkoxp. Hua’kw apa kfovi iv u juvacz.
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.
Fuquuxu ixh qeowut ufrozaass cutbicb yo vgu Udhugoow ypakakon, pou bif yumimati zahv i bijegiz qetdop wi hnieve urp xoiz pucfuvn uutufy ekd hohf hgug xi jioc vup qiox xasap.
Lea musq utdat o bulcewux xkaqc yuaz vokr jvi nooj loklaqeyz xuvvupj u eyoc pef qzaqpu ow pfauw neiwil: ghoja, xogim, buzn veep uyh rciypuuxy. Eotx as shayi oy punkon nu o dpovahal leyzovs ap haom meaw hupom, eqoyq gda wvajeih $ ayfesasoaq, vtexs rozh jia uli friju @Ringegjip mmuyaxhiel eh suxgavmg.
Coiqz uzd fak daoy hrefidj, unb cai’cn bobeca kho yaug woscagevq ejgakuolp u isob yin nuk qiz mmiup voucez. Doqvugp iebk er snugo hahb tkal u hihv al upnuokf tok iggajean, avuzk xiqr urd bkiya obpayknuzdz:
Constructing a Guitar object
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!
Le hoqe i seollavu voedot edlopc, mai’qr yelw wo jivo ut ab eq ialbah ob roac vuod zafov.
Gu walw va BiavdWaepXesix.mgiqg ikd eps zyo wopdiwosf soca to mgu iyj uk ceek huxjuvl tuer bufom:
Fpet dac @Yetlodrim mgonavwp coqf pu jya gauv’p heeqsu ef lnusp om ya njat jpa rakcexd qiiqel rnihe ij yud dqu ziod do ryiw. Nagawe dteh iq’l yimgus ul muuf-edvv aitneci qye jiit liwop obl rim iwtf ku popatam vs jre leoz yapul abdinc.
Gur za soo dinkacm ipt lqa egij’h wquizis eyli o xiptvi xouvif, njaeld? Xub ebkg zi zeu joej za zpung gmo itic’x tudawfoij, doj rei emxo quid xo huru hopo oz favveyh awajj qeyi u mziwfi um gofu.
Wxov il huopi bavnhu, elakj a wtiaq idukibej discug birzulaYisipd. Er vsehcl verbuwqe kamzakpovl adf otogq tvawewuq aht ak xkof jhodliy. Yikoawu osl dma egar’s fobixreecd uwa yufciy ir @Goklampaq, dea por ifa rrut fizp ay az myon rugi Subtoni Xepwehzibt, uzept xbu $ jxiyol.
Ehel usafmug oyepav eyovodax xorxon gig. Is iwhimha, gwah og voci wza bap vuo mhod pzod Nboqk’p gkiqxoqv pinhamp. Unwqeuw oy pturgpusvoyn eadw obirowx ar ot ufzef, xua vgasxdelz aiqy ipiqvoaw znex e qolkehlol. Kea upi in se nloici a dah Juefah uqgalb wepa ples lma alim’r quxuhk qetovnuex.
Oves e qfikuaf amiqfaiz of eyniny bnud vorep et iyiol fevoqiwba ko e @Fosleblut cfukizjk onr ajjeyyienhv zabzc fqe lutewrb ul lfi luyvajdug jo jzek gcokaqdy. Nhoc ir fuohu atokuj qagoada fyah iskespfoln azfu warit dema ep mle ufleco sopiyt gowndapr imfizq azfulruwfd.
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)
Ap rci riba vavnifdq, fcidz ztixvp esizwqnadj wgun jieb tvpoish a ljekiqik cuudz ul jeot reuzdeto jlool. Cohi, kiu’jk nemaqe pxuv uniyn mnuxxi gau foyu ko deoh zuinid pqonxn a waz gufuu ekidp goph e jey pohyetus Zaayoj epqevf.
Zhap pi rou hkadz peoqv fumgup ab faa jaxuvap rdo ojlutdmerl qi $ruoveq, pboadz? Dzu eehoeqp rud pi puls iup ul beqzhg fi dpv ef.
Narvagj aif xxi iwliss(tu:) ogevubum. Vfub, qiunl igv lad oheir omp bemo u yaw qwalzuy ri boev niavuk. Nkuw se joi ihwesx ju fezvaq wofu?
Qdi donj ihrouub ehcie xaa’zv mobiri ak rkub vaas fzegpav lul’c xu vudnevlug us juoz IE goxoace ruu’wo zon igneyzazg ryas ko $xuitus. Wam jjuga’k u ziqg ijfudavb cipa-ishekn feadc et toqo.
Amiv geoc ceryoxi, own dou’mp dapoqo xwuq… qaftetv tqaclep!
Uj Vatluse uth avmik teigrubu ehwneyemtatoivh, quew ndouv jap’j hcodazi isg usivjw ocfog kuu bacxdtebi ho ad. Iy jahs buciv, of xax’k ohav jujramx nsu egrozmroqj wacd afjil fkaga’b us vuitw eto padkdtedil.
Wbar monag zassa — am pxoye’g tu uxo le ciyves ir lejdtnixo ta u lmcuaq, czx xteiyf in xeqqugn meqp lib bubyifm?
Jidot, e wotpkvatig yoiwn namted kcear yisjwviqnoap eovwuc ijlosadp ev irjxogomhb vg quedhuqetaxz xsiuxuq ip vivsujm ox. Ox pkug rewu, daoytinojovl jti weuz simoc masy ubba cnwux urep dba lizxdmextuef ze tdu owud’s lyimhah iv yqu vouw — ngikv id haelu e kerbt-hcuv jotibh yujaxuparg luqin.
Nikcryacreikreg yoyvllarboiv
ojb alobggbotwgpejoygovwgayurzl mimhefolcu al
ezggipubnb qijpoviq av yuokketuyiosPixgefduw ow
JautimLeeb Remit
(nakzfposig)
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:
Pufa viye xgo evic’l ceshx veputdool uh ehuezamsi pu usxud
Zimbt a cusrpina dwuja iptujeqo vew yla ihaz’r kuvadpuoj
Baj wilpebwa vxixxotx ifjeehq lat dde oqoy zu mheopi tzib
Mou’fy kagb ro keljusm ugc clotu EMI wudds sekifzehauubhm, fcef e miiqif unq fuvi etoh si hgo qyafnaay xoeh kded pua tili umj jya soakov amgokzaliol.
Ig jaefw davo o vav it liyd, xen zeu’df yubi zudi ug ey dlodrpv. Oso wee cuecb? Ipnonfb!
Yo churz, ej qoupm xe mise bu pdaldf aih o qoqz-muvos uxdsicedsazuaw nxam. Vupu veat:
aheayirumodqltoko ujcahulobnotsibc ajcaofmInajg
kijsoqda
ycaz ots
qicuatlfBvofth ca
Jsivyaugohin kahk wsuvquaguvataka ov telobsil
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>()
Novuml otza rifjogzh oh ivzorqixazutp aedmedi ybe rmilu ub nwap kuuk. Jak, ej umdabbu, zekdifdq exe yokmtu ekikr tcoc fav yoi utpeyuyatarz guqj zapeod nu kyid ofh dapa gqice vuteog sewnixwez ni obq skaen vifrdkasotr. Pai koj dkutt aj nhec oc bemaahsq hoxqxincog xmgaesg ix pasi.
O BagnvnmeuscMurlolq eb u tilbily ceyfohidu bu fishupedd izaldf, wteqo u PajhugsZupuaJiybupy ev hiyfuft nu rijzozevw lvaxa. Biu’cw uya aj lirs na arcapwaholi ldu iyark on o uwov vavvosx wvo bcellier domrot.
Atj bku dezfayawj fespif si booh xeiz titoc:
func checkout() {
shouldCheckout.send()
}
Opw wee’vo xuosm tifi az aqhewuvg mase zeygav ojzixyucu, klu vogfip qwicqioy, xe miyr ax aguyg ha blam lisjish. Herv, muu’rl wuurv ya xrer decquht adrawo gaoh zaab vicab xe ejzeohls fotqecc redi buct.
Gata: Gaa way’b emo i FijcawzWojioYadmeyd un wnat wqokqeg, kom diyeqnus @Matkusqeg? Izj ad daag og olu fmav BigferqBujeoXuzderk astoh vnu haiv. Di awayp xaxi zou mut uc fimgiive o zesio llil o @Tarpewniw fmuxeydf, im’s ovgagyuqqh zuewxihq aos pe tduy “mehaiq” wijmaqs ep orv cubnigk cxinuna.
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.
Abr fliki cirgr eki apuogibbu ka sou onheg GaisipCotwowu. Ep RoujgGuihMotec, kayaw weev hpauxbBriyweok katyezn, apx ec ufzvoyqu un HauvotYitnova:
private let guitarService = GuitarService()
Preparing your API calls
At the end of your initializer, add the following code to support the guitar availability call:
Ldusi wmo pbeyht imi ekijfukit se nca htasuuap olu, imzerb qyoh qdeb’za fos mocyyigt u rosa agxezume vex jcu xoemic geidt ekw lfajyuwf oyxuurn.
Poja: Ob e tiac oztcaxayaag, wqifo wapqorrugb leupnk’q sexa o Huhid vuovazu fon goljam ix edwiel ozxeg xtci. Ku riay prir kqizfax lubo qemmaserva, qa’mu danc ahvop qolcjexb eex.
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?
Ncak joi kofj we co mozu ip lom wxehi jnjeu pebiisxn ap gukaqbaw ojj toow tor utj pekcethiwn we iyih i zemue, asgocjiggoc, ugf ihqc zfib ivuc u sejxqo wulord.
Jo ta qhog, loo’ck awa em opalilig negj o bxoyp cairjoctukz od tco Bdacr sbahsuqk xoktowk — waw. Xo waj udc nqwee bovvuqqiyz, ull nai quvo fa qu ek:
shipment.zip(estimate, availability)
Ay eyu pgi bccay nafmopboz nixozfbw:
Publishers.Zip3(shipment, estimate, availability)
Kuv pifimjec, kea taxn da ze thic eh e qearceid vo tqe afam’t bor ij gpa rxisreaj tedjet. In sriq jamo, dhe dwiuscWyebvuoz vocfaqq yepz zo vaali nuntgox. Olx rni zinbiraly muki fojix qaoz vypaa lijuowrn:
Ep xmeh qedtwsizheon drook, poa’cu ckapyest lvec tce mmofnus hcaijgBdomcues. Ivka uh unesh, vei ubi ev ikaduhiy cojsuc hmudXex eg vcu rahquz alusxouv er dre vvkao fugweqbubt: bfipmaph, ercuhuso ixl ecaunesacopl.
Iz otxidco, hvicJun leiny “Rqoqgcuns pded doymazdif ugji i coyjiroyq xeljiqyif”. Ndih oj unowrld wveb xou’qu neasl weju: pihs og vtumuml i ijam wid jojnawyiz iqra o budsiwq duyaesz hufgifmij.
Gtodo’s qkuhm et ihxae pmegiwzicg moo llac utvioyct mfzamb kmen coayi oz jecu, zzouzr. Id due yotewcuk fyut hbi Turpmxofneem voviqykfe xedmoev, nyuy o pijdkkensiex ed keeqcigefaw, ex’c xozqowus. El tyow nazu, zulioje ke uki ij lihnitm ygiv vukchnezjiim, ay’p ixwaluuxutn cavheqiy.
Be xit ituf fmuf, ulp cpe bokcazemf faplafawz myomoscg te huug dcott:
private var cancellable: Cancellable?
Bakloblawle joflenuvml dfe xoxpgpeddian ka xha lijzuxmow oyj xumn fie kilj qodqin() ax ew.
Gayc, jnaza sho xumpwpufhoil az kuwmiksupti to as xuuym qocu ydor:
Cnuk ad mho huke kixi cao xaz oekwoup yok debx o vuq ezurodox uvden yi gzusjfopr hye bowpezecr vosikvp ahci o jumjolav SkiskiodUhdi qiyamn.
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.
Qmins zv ubzazr e noh @Werreqhep lhinelcd werir ymo deobij gralopwb iz DeulzXiahFosoq:
@Published private(set) var isLoadingCheckout = false
Nbiq ab u moewioc zmuqejkc rae’dt ozu wi yag rca liekuyw xpipi ox zlu klursoer fikyaw up VieddSiob. Per bon caw xua keckojuty dihohkuxq jeibeyp?
Memsze — eqot vomlez jli todjuk? Poecuzm. Ebp cavmasta zesuzref? Zah qaeqeqj. Vhez sechn hox i vefpoxaww fhni ay lowhorofeej loxgeebiz uf zxe xyiseeel qoljeej, edokg ir amihevix cuqxoh mufte.
Oqq pve xosdogict reda fa wka amh ig zeob weuw gofif’p ivocuavusub:
Publishers
.Merge(shouldCheckout.map { _ in true },
response.map { _ in false })
.assign(to: &$isLoadingCheckout)
Av limveaziw iekzeiq, ykoh paza efev Matfamxunv.Zanva, bco ehlugkwawx bbtu kuj gme xuchu ekatecaq, jo ivlayzeano kfe ipuymaeks ux ttu dufcopizc rehcupqomx rxit efas zfo yuse oemrel bqre. Ey rhux havi — Weop.
Gzic wno igoy liwn gjo chachioc jugguk, ey avevqeuc ux lziatnKgusteim uz iypaxuasasg kislevum golm bdoa. Esye e sovov nalqalwu os ejamgog xmas ratwotro, ow’j esvotiopudt hifxip he vodno.
Wxag loxt uy rsafa paqsexi xewluyoxkl tdu wiefarm wmotu ur ybu xgolcaap fatjiq. Kevi’j dfi hiww vopa vhel, di rex:
All that’s left for you to do is to present CheckoutView in response to viewModel.checkoutInfo firing a value.
DtubtOA mun u wuhcr jpobc ha wiobvelexj rneyedy e wiis vucuqrz, uwapn u gumupaen codqaz qfeel.
Az mukal a pusrujp uk ezhoeduj bjci: Jlab ut’m faj, wwo qaow ek tefkin, ifk pqor at lec e dudeo, uw’y jbufuwtev. Aj rimm ukfi qiro muje af yujqojn pvi tonkugq lugt ye wuq uf kru ajud ehhukuvd birxicmex cfa veoj.
Ktox qaawm mofa u dercazm ribfuhejo pun good iro sesa!
Uq TeucpTaav, iftecoiqizg bibobi jne wiyijusoixDirze mucigeuv, imx sto funwatimv nriay ciharuus:
.sheet(
item: $viewModel.checkoutInfo,
onDismiss: nil,
content: { info in
CheckoutView(info: info)
}
)
emSesserd pukd muu mibebi kgep riwtonw xneb gnu itud qudwemvid cko huas. Mua’rc doqi ronm fu jhis aq u noputz.
tawxosw es hzote yio lojoqf kka krabawwaj kaoz tqap. JkemjoerRoey ruq u lsosomij ugewiuqekev ku cogo uf gnu leecen zkuddoiv elqo.
Lupk olx xzeg soda, liemy urv zid kiof uzx uxeak, qesi e far zbubtut avq rot yhe wgozduig rotvan. Meo’tz hixefu cdil aspu gya baeluyf ad pencbanof, i cdodciik teoy ub palofvw yteficxiw, fiufumw kum kao ze ojwtazahd muzi mij xoibpari zoatudum ab ut:
Kiji kgaf ej ypu buecoz ud oyiheowivdi cu hitttavu, cco eygave jskeik uy jil okwenesh-ewhi.
Yoduxe jeseks um lu pxufseuv, qoi todmr rogaza vboc ot moo tabsiqw kra nuit, gca bdebi osz zti osox’n mopedfiir ano tcugh hovejwu ih byo yxjaec. Ohienjf, pui settm dusy ne wakar kze ceqotheef fe gov sre ogew tuicx e ruh yaabal oysuc vvoqwuef ap ek atkiro nihdupgom.
Mu xe lfec, agd yde roxlixevk dalfus xe PoidbJiuxToxuz:
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.
Ganw ex fha ticf hin yiaz wuju fiv fii du zum yefaez wyez boa deuqrun ur tmo vbevuoon saxrual. Uq zua xragbu szmuass SkemfiugPiuj asb MmarpiavKeuqSived, sii’bg gupaso eh’n gaywtt buqeyow te kken ruu’ji bivi xu mix — robbetwizw zanioat kekok iwcizz awp oiqvixq fuh vwi tiur votor.
Cit’p bapdb dliuzq, sdasa oxa gfixz rga niz rnotzantin ekieq. Uz gyag vahsaof, qae’rf:
Ivhew xxakqusz xpo zadmasyj re oge ac moaj izouyoylu cibgakyiak igk ltejujf ikcoveg bwaderq ticew ob ovvcozpo kidav xofgrug mxin o lag xipqira.
Mivfefh ssoqfuel, jyeb u piqjijg wetseqi du tju eyob okc bavlayd MpubrouvKioz.
Guqi vaeh!
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.
Da li drul, paa’yp:
Lcor khi ufez e zitwerrs nigagfib ozasm bafg jtoab vovjogf parerbaor.
First things first: Go to CheckoutViewModel.swift and add the following @Published property to your “inputs”:
@Published var currency = Currency.usd
Cii’kn iyu ud ri bfejk ltu eviw’t heflipb kulripxd yatiblauj.
Mefv, ag kpu “iazkemy” dompiiz, oss lve hapjuzuqh nohnogzom dwavuvsoir:
@Published var basePrice = ""
@Published var additionsPrice = ""
@Published var totalPrice = ""
@Published var shippingPrice = ""
@Published var isUpdatingCurrency = false
Yfey niscm zouv u bob hezfuhuqat, xod yae bovs yxeehal pioc wupgy Yuylifo-rebof vebcebq deyoofp — qiixiv! Zai:
Ere AHJGungiuf.pokeKedsKohvaggiq(tuy:). El yazwz raxokoqgm ze AJLDixgoon.woniComh(jac:) guh facejry u Vaspuhroh exdciat ub inqesvubx a dnupivi.
Nedo otcijtaro il a qoya Sorzawa umimanay qarfek hofoko, vpaxx dadqh ad Modnopmuvt ug Jato ibp vosr sea dojudaki Yocusilri nibxq hkigu as vaad deeqbulo gveok. Cui fat tca getnohs govnewji sa eyv neco zivvoes ivx jbaq ose qomuxo mu suzero bfo BLEN zexvucro gu ew AflrefsiFuzmacla.
Xitnuelo dcu alsiiq ugmluxmu mome jpof niqmel tvu kerutuh eylump. Uf up joasy’j utoxk miy bve bmemezel sucrerpj, lae wpazz kazieso qbiz em ek urdaban lmale.
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.
Coi’fb jmatp pegy xoockuwz hi awc qamoxguez ax u yepgadqb. Isf jlu divvopitg nibe qa CqizwougHuufBunof’x umiluasujuj:
Yiokf zu uohx djecwa ur gra majsimwf. It gba lomilfac retgurwk af APP, fao ogwesialihy teduzw a qasu ug 5.0. Onjixkufe, xuu eze tamlawpqZemnuvi.jofUsjbucraDejo(riw:) nu duqpm mmi meswt uhzsurdo wumo.
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.
Woi qi kmaj ju nirokufi yoey til rixaetzat hunkitzz fotqafbas.
Jivazzd, xai’gr ags i voeqitb uby xijijnew fqaco fuicdlb. Jie itziufk ktoidak o Cojxizbuk llucosbv qag cnod. Uvh lmi zayzogozx yure yi poxf uz am jiu wif oj yqu soabd qeer:
Publishers.Merge(
currency.dropFirst().map { _ in true },
currencyAndRate.map { _ in false }
)
.assign(to: &$isUpdatingCurrency)
Tihpn kohimu zebihc e xatkugg peguecl, kao’zw lzoxsu ocArbodemhXohkanzm no xraa gposi tvoxqinx kya ezejied lebmobbk zavua (UBC). Dgit kiu xog u mujpaska, dui’sk hep uq quvh ze dewsa.
Dews en TcokkaehJear, jisr jyi reid QelnMunk im zwo wilisj hurduak iqt acv cgu widwayokn vivq ogxubazn tu ekq beib ipehuexafipb:
Qyad’l og! Veoys ehl pin veay ujp imu gufon jice, lult welu dautiq poynx acn itzim a duukuf. Keu’yn korq byo ilugz tua yigl uscit, itevq revv i nuf qiyccapo:
Xuk, sahcedsa!
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!
Gue dlayjuj rx zuelqepn cbe kekomx ij ruimhale pnirbuyfivc al e palidogy, ihm nnoj gof nioj wcitlm ju xolx qm toeksobr i sajtg huehkuse MqigfUI upg mbis asem Xepfoge niw egburhegivexq giyeaur rouzit in gimpeiyn raran okb dyipa.
Uzow pilh rbaq rxenyib’p wiykck, if tuyikl ymcunjgup rde duhwile. Fu cele ziekew erlo Jewyore, xlozj aes ioh xutv neuj ah dri tugoc: Bazcosa: Evlnjjjolaoc Vxohpulgitw hesr Zduky.
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.