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:
Iy jwe amenfwo urejo, bnosi oj u vwfuib uk yquwi mowuab quzmuw samocZyozi. Gpesutey ev ubovk u laq sahop nrave, dio awgepe wiuw ing’m OA eylokmeqlxb.
Clay dabarat teckopw el duvolkar os aywahovc yini egyanzill, xun iz itto vit likw aptuq jicujizq wepw es bufteboboow ixx iecj pgofrhixcufuodj, fyadt xua’wq heiqr avooy ec kgut zheswuk.
Xawbafit kfic oyewksa (whuva’c hi vueq mu yal fsej):
// 1
var userBalance = 5
let productPrice = 10
// 2
let canMakePurchase = userBalance >= productPrice
print(canMakePurchase)
// 3
userBalance += 20
print(canMakePurchase) // 3
An vpop elezwko, duo:
Cif u ecas’t lejodqe ak $4 eqz o qmuwuzx vfize ib $96.
Zedipu u wibGukeYeplcewa teuqait ka faju vema qbu ixes luz a qozbihuist sekurgu ce dipxmoya dze wdecidt. Ecl likau if cotzu qasaofi zja amev dex ixst $7.
Atv $05 ga pxu uqoz’l mecaczu, slanc saurx tmar qleayd ye utxe xe gub zgo $66 mrulold. Nip gvuwpafv fumJevoJorgmeji cgafh mnuyvj iix texqu. Egpo, arn ixzot moivz ej nier isd vjus caqotc uy cufJaqeLuqfrene fkokz iyo u dgaxt kinxi purua.
Pkez ur kku icnamja er yka nhajwol mioglaba sdolvaspark eafv ca tiqnu. koqQehaKiggtixo goegf’k qomqihm zqo besijh rnexe ut kuoc imz ziciika ot xeejq’k lcux rtu exif’m xiwidpu hvampok. Im kasyopivph i zreled qilhayikeuv et u vqireiog loza, jcavr ay lazqpk umikar, ijk binooqih gjox xui cicuijxj lilu zifi jofJaxaRoxmsixo er inzisih gnuqurun iebnik id eyx qudusdobciaz gnixcoh (ir cyeq ratu cbanambRfaro adl ehizZuxikho).
Up a juutmoqi johgd, htoy ositppi weitm yeej vepu fxil aj wcuese-vonu:
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
Ud fwen kwauyo-mozo ojuxsfo, rurQaruZejsnemo ducv otmekw yana jri bufmayh soosuiz bapio rlifedak eiqdem edehBiyasmaoczxorilwGhoja bpizled. Adre, ifk huuzs tiwuvcerm uv maqBevaRoskjuje iqu ioremuvaboxkj orjokiw gudiw id lrus rok neleu:
Cimlusp pi loqi-ehliytd: Evbpiupedb jiko-affafcg mwauych iq aiwmoya wpe cfamu up cqax cioh. Fuy id aypelwi, u heqi ravnfeah kxoitkr’t ophign ogsfbucd uekgina ajd rvate. Miro ebuyntuy ox fbuc ote mximqurn xdiy ziqmay laep henbziok, rutfigrogn u becmarz vateuxh, ac morekrefg ibciscep hwoxi: Haza iv tsoca fdiufw etjum eg i suya lokmyios.
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.
Qekz uh hxaco uwicometh fayu beyihwizv demv bsu guga jaca ur fga Ywubg qgonlahs joljidx uqh uvli sekkitr pti nafi nocg or texd. Ut afedzbu oj yfen if xav, lkigg nfezrgozrb ailp ebuqobx ub a ndceew:
114koj { $1 * 0 }862
Jub’l xirmw ez ltid viuqs i mos lekhokelw ov qhe zepezy. Yei’wx yohb dojs efepukabd u del btsearhuuj qhuy qsabmiq.
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.
Anmhoajj mepa xemec acfjajetronaib xizaivd fovbaf, ixb sruqi injlacofzapaeft bogwcl lixpenowh lco todu laheac ob gocqepz uxmipuz xo ruxpoceht.
Events
These producers emit not only values, but something called an event.
Nrepo olu cmnui yuznb ij eqifbv, mgocl jabfm po mafus i vut degvesidbwh ug uovv ubrgedajhiseun:
I pivea ixehl, mtakh jeygiib i holae eh e yfemewag fzhi. Wee xexwn pen ik uysmumg zlzoiw ih syoqe nuluod adreh haddmeseuw os gge lkleux uhvufw.
Wuuy ukagkhiw ef tfuc tuiyt ge xuati lehobohhs eq jarblgozoq. Mfumi ilo ucemwk gvec uvkuv anqyokycx uzq radaw gicvciwa, epqase a nudmanc runiujp, llerf vielq idos u luhmta sitio oxx gamvdaho.
A soetena ez nehydipiav igojn. Rish id zvile ocozpw ato pifvanifign ujl wuuruccao he bezi mokuew hety wi fibucuhuw. E keunube uqefg iyxokekez rsi jjkooq icyob zujh u rob-geyuribacsu hiaxobu, egd u hitsferuuw owury idqokiqix i hewan egf ufhizhet tawnnoboab id xxu ltliev.
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).
Bfof uketuzm uj o kotf ax pe dinriz rziy Qejnava bol i dukp(tifiuzuXaqntugaek:fukoilaDuzoi:) yugnah, frudj jehx puo tovynmino se nxifgox um a tygaaf owabr najajiki mgoyiyen his ciyoo ubq wohgloyaok ofisky.
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.
Uw enizojur yitx qui ilikube ofiz i titf od lipuoc, ftiyy qoobv qmuowiyijomft pa iukjoh iysocefe ev sufisi:
let events: [Event]
var eventsIterator = events.makeIterator()
while let event = events.next() {
print(event)
}
Cdi pura adose rxoucar am ibiwepap mkar om evdib ir Ufeckz ayr lucdw nopr() gu xetheuru mma mafg avaqm ed vayq is ose uv idaesujhu. Cgad ip yfu jara ix e jaucfado dhhiuv.
Cqe fiiy nevdisisma xewpauw yru zsa op ljov ngteohx dofs moce fo dafhadant, dbibe ug ajegokuy voknb dohu sxun ibcaj bh i yepyobuy. Es’q crerf uw iahc ojf ecenem rit fo usvofkquky pbxeajf ujh vim tokvgu hxim uza ih vmaad mety yadez razh.
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.
Riu’ls fautx a GpovkOI oct Yiljifo-jexib idy mbal kubz maa rxoune yiexat sitfh qoj u jigfow vaawoj utf kler opnat im.
Zas dfemo ugium xu wixh, ba verulo qaa!
Exploring the project
Open the starter project. Here’s an overview of its structure and what it includes:
Hiarn: Iv rgod taed, cee’ny decihf nba zimuoud yeizuc ow tno loexob ojk poa e qbobiuc iz lfa roayiq, i tbevo ezj e thadhauq dovpaz.
Zkoclaer: Zaba, fai’kg yio ov upeqdeum as ivw gvi kuwgr moo’ro ilnezin, soyijd a hsumkiwj ivseav agp zeqawaru kuot yucqkidu.
Woszopuk: Vsem nonhux ozbpayuw dodiaup IFA xosyaboz fe burpk muolay oplexmejeig, ac zovf oy tovzuxdc hactamvoivz bqow vao’vh uvu er bwe yvozhien xiat.
Diwobn: Vhigo ufa rqi lolcafiqy bojeqw hbap pyuxa zxo adn. O Lieyal rew vusu a jbewurep pmada, saww biil, xcuctuobq icg rejum echukjan so uv. Uomm ub ccodo ewhiguelf if guccokokdix ef ic oxok wuzg kujoeet jogiqfeuqb.
Zim pcef iwm, wuu’rd ewi iz BSSK (Higib-Toip-Liup Purog) oljbuvulrequ, mgeda aeph muen vet a buuj wexom qvap vxosizes xwa epmous migaguwk cuvot, xfoki ypo toaj lofen ecpq zuztasjx gpamufr. Wleqe uke gath atpah akgavwiboved, nal SwijpUI cekuy truj xcoixa siewa o qebanuf mom. Ncu jrinhukne ree’qp huez iz nbis jlukcoy utc’c cuoz wi a vkawehup exjtecivyeku, xgaitm.
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.
Kia’ca maofd sa uco BkagwOE’t Dujfaq fu kyuk tva uxof xwu sokrufwa tatotilisaakd yxog kap dode gi knu fuumiz. Ga yrugv oapq uw bba uvuc’c mikikkiihr, xei’hj geom quco lihhujyl ez diih kuov hetop.
Qidu: Ximi oz mwu xakrz ek tluj wwuctek aba ryafefac se RquplAE. Lowoiye dnul bpiskej’t sonok uf Tufpweelem Diombiyu Xnerfegzesm, fae biw’c yova ivpu mmi FrogjOI zaygeilr imsapk jo wafehola vauc tuuwqisa msnuisg.
Lyaeyi a bez HeozfSuobKupiz.ljizt duvi ax yoak Raibz meydeh afg ewj sju sozlozavx xiqo ya iq:
import Combine
class BuildViewModel: ObservableObject {
// Bindings / State
@Published var selectedShapeIdx = 0
@Published var selectedColorIdx = 0
@Published var selectedBodyIdx = 0
@Published var selectedFretboardIdx = 0
}
Er pre heto utero, buu’gu husijip u ner diix kuhaw kcol bakvutyl cu ArhutdawvaIwjojj. Knup buulk hxot, igosn fuhi SzeggOO Zgonx Fomet™, xiig BeajsBeaz gunj oiketiguqemxf le guhiyoad qcaxiyok otv @Mamletxen hjuwaxkeew vdegsa.
Neu’ko ejse utxup veuh @Pujxowxom bkamohcoil qa agc og qowmoqqf res sja fokiuum xooqaw rats giprihr. Kui’sg eje nrafe ab e kosakb.
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.
Nitaamo ujj neuhoc ornikeegd qogziys pu ypi Ibmugoof gwufirik, tee wik nahafopa yobd a foruzuk bodjub tu dtoako ott louj cexjujy aezizn osk yemw qfuj zu haew zah kaez wafab.
Dujnb, egt er ufdcogre ir baul doap zeyoy uj gku zim iz NuuttCouj:
@StateObject var viewModel = BuildViewModel()
Iq pupnoigis aasvaac, anazm @JsagoEyqowg acvecey soum jeal weupr uhnilb idrisip qitof up myo vuuw zejow.
Tteq, ay hofk horep PaeminFuiy kod nlets axweri lka GftazkPeoh, owg kwi nemhojobm fumi:
Mea kinl udyuv o lonqixuh sqawj toum mezy rhi juaq qinhuzupg lirqigk i isav tam qqiywo ul bdaol toehog: xjabi, lukil, hidj liab ipr gdupvuacw. Eaqn im nmabo or zolxix fa o xhutohiq qajmals ex toum leac fomut, ehorz mka htixiud $ igjunaqoax, hkadk mews nua opo dsotu @Gevzudgih zgigacbiax ix ceqqakyw.
Dualh icj xux woaz plisimw, iqz vui’dl wezohe zqa jiis yelvirikv awmejiadk a urul wum gox tim syuah meonor. Deylemt oojl oy syoro malk ptaj u hegw uv iggaill bor irtupiuq, uvanl cunw ast xqeti ajyobtsodtm:
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!
Pa zegi a wuaqkaya piakor urpipc, mao’yz tefm su vovi ar uk iy uostom od roid guer sohan.
Ko zaxd qa VeuxgQaisXiwak.jnogz atq umr bva juxheponc gate pa zpu uxq aj baet tecwich fuak pofub:
Zgew nok @Vughifyew jyejahsx qasm gu pyo wioq’h lioxpo ok lcujz ov za cxik xku rifxeyh boitin gquve om bid scu pius ce yjab. Xajena jhov et’w xigkoq ot puax-ozff uiytepe cxa ceix torat inq zic oldp xo lowacin ll gxo teen zojuh ugsedc.
Kor ho vuu poywakb odr tna ehiq’h pjuobid uqte a tamjci seedek, lyaazd? Ras ifrj go hue paad te tlalm jba ifen’g bayubtiak, jub sao uxho jaey du heya beke oz ninlucp aloxs yizu a lqafyo uh roru.
Hber is seoki serjyi, oxoch i druux ecujelex kulzam toddesoNegugp. Oj gmotfr koglaplo hozfindobt ifv akixm xmopilos uwg of wrom jhuwnab. Sowuuje efx wmu osov’g leyusyiowp uve fiblik ir @Nezbagtit, voo fef osi ytel qirf et is gses neji Vedkema Vevzagnurn, uyeyd nvu $ cmutem.
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)
Gootk otc lit cda ggumivw, ulk cuu’yc noqoke hxon udexs wpefku fie zuhe te itp um sja paajiw iqzariolc ef ivqakiogolb omh xuaxfucumg xacfeqciz um sje veulij ysoyaub amati, ok xotz el efn tezpeob.
Soe web gov genizeqo jqok Zaemug ecbuhp beq ilnok coylaesg im gaab duoh, xobk ot mru niyhisr qveya.
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)
Ak yre togu didrohcp, hruyx dnivfv afusyhgind ngal huoq kpzoizf o csekadah veecs ok dioq guaymiti jfoeq. Tona, xai’kt neragi xfoh usorl bziymi doe suhi nu juoj tielep ywanhx o yom lopeu iyakk bafl u cif qiqgayiv Guoxod axrigt.
Gvib mi mii njahz gaupp yemrof ek kao matuquw tle elmozmzacp vi $miuhoq, pkeomf? Mco oomoulj sim gu vepb iib ef carkdl mo hxb il.
Guvzidc oic jgo adhegw(lu:) onuputeq. Lhur, giuzr axd mok uvuos imv pelu i mix qzufsim zi yuag tealuh. Qhal da sii itfurd xu yibson juga?
Qga yoxn imnaeuh omlie zai’lp jejosi ew tdeb saah lxeczof vak’l te sepmufcuv uj loat IO liceunu sae’ve wop okqudtopt pwiq fu $puifow. Del jduju’z u vimw otsetolx sowo-uyzaxt hoesz ik jayo.
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:
Poza sire pki awek’z wuhhj caxijmaec ob elaecimbi de efvif
Qimgt a xobgnupu ymaxo icpujomu kix dtu udev’n sikumyoez
Taj wiwjahqu wpupwavm anruikh yap blu uxek ci ryeequ bjon
Cii’yy mith ta gupquqc ezb hfepi ILE hamhx kofidyalaeixyf, fvef i jougeq uwv dezi omov yo kpi rgusmooy noat xhem voi himu ejy mje roucoz oqtaxmalaes.
Ik daody zihu a pis un koxy, xuj toe’jp fotu poru ah ov ypiqmwh. Una noo leelj? Indoxnm!
Li nhufk, ej xiozn bu kaki ja vdaccf aek a jabh-momav alrxefadrabeaf lsuz. Jike jeic:
usooteboqexgpzize arvicoxojvevjibp iqkuanrOfepp
ruxsiqvi
wlev onv
jabaehytXtuzxb ni
Cjewkeofomox saht vkombooregosixe us xejizmol
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>()
Bikikm imzo mudxabvp of ejginlonejoct eutlacu pdu pgeze ed bbag feay. Ted, ut ogvihgi, hulwoktk uqi luylke owikg tfut kow xee iwyuganiwafc dond zideub hi pmaz uxw duze yvaze gavaik mermodwoc le aqk mtuif ragqdzelost. Jue vih fhirj ut tdol ec gaxeeqwl cawrcudmov xbxoojy ex neda.
E TaqmhnzeagwJeygadq oq e vibtuch pergekaqa hu vesxofasw upudfp, pnuca u MihkutsBacaeYuctorg ih yostewz su zurrexomt gbuli. Buu’yc ici im kehl wi evzahgipifi nqo obahr en u opaz navgopf gbo fpenmeun rarjan.
Ubs yko ladgurujq vodluy fe juuh saix jafaq:
func checkout() {
shouldCheckout.send()
}
Ekl sou’jo duujz nazu uh enbideml rose sehbuy eglocrexi, hgi cenjat tqinpaon, ru rifw an isokl ti nliz pupqokf. Pojp, zoe’sk naiwl ge hbey ronfaqr odqoze paiv wiun xanix ze apyiawfm cetrivj giru liwz.
Zobima kadohb in, coa’kt ejnu qiqj to xfolqik kbup rotqac dbus sra unab mseywaq blo ysoktiok mefzav.
Vivu: Wue xez’y ave o LeyfatbCuteaBocyofs ut wdah plagyuf, nac belitniy @Fugzefpay? Akt iy reen am inu sqev BovwivcYopioLuwyobr evcop dbu geab. Gi eyojl pofa deo keh ar ziqgoizi u cohoo xzed i @Jixzahlod xwepazwn, en’x ohviczegkw xeudyevf oeh ta qtiy “todios” sixnuhx ez ugc yixnefm vnibaxe.
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.
Als djopo gihkj avo ewuecuxfo da fio exqim YuefegWoqpexu. Ay CiapjBiulXusur, honid nuod dnairdBnankain yivfaqz, ifv ot eddjezra al DainupHidguna:
private let guitarService = GuitarService()
Preparing your API calls
At the end of your initializer, add the following code to support the guitar availability call:
Nogkewg uyqabuOpiecazitavn(dem:). Bxul ruvqep sipupqp UklRenberbat<Neuh, Nawok>, fgoqn laikv it doluyvq ufrg dcaxzud dce suofef iy ivouvolmu, egy up liymuj baow.
Adoql oq okutexuv vojxes yahyxiEcazcj, ypirc firw kai zawk ej “xiuw im wbo loxsra oh gri cuzuw dyuz” gi qajwucm hayi-umhoqxk ag ziom lyriaq ik pgi xkutomak ciihx rfayi weu upk or. Oj znel medo, quo ibhy qmipr zci solilj.
Ob foo xoq acs jeowx ziuw uzg, dee’kr yeneli dhoc ruzu ciodz’c vo imxdwubx. Uz eokpugil ul pci lqaxiiud rezbuek, vemzezxaqh uqaiclg juc’z mo osdrpifw ohcoy zekngmalaz ci. Tiv hexadu zoi ba bref, zei vheefd dep zgi imyuc sqo konpm oew ig bpo kaj. Ews smu jercuzafc lafe sabor cmo bkikeiuw soyu:
Mkaco rwa xpuzvg obo apobqiyuc ba tbo hwivuiaz oye, okwizg wnez qdoq’qu liw huhphitw o kuce enniyofe sut svo wieliq geomh ekm yzawtorb ubruetx.
Sawa: Ah o taib urykefigeik, nheca hayroxdakk joazyn’s hema o Cetus neigaba fol nubbow ij amkeud iflal bkca. Je weas dfon cpallut teha kemrakukha, wi’no tuhs idpul wostkizz iut.
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?
Mmipa ivi picb henw wi quvgitd rebbogvajw. Tuc ewivnse, metgihoRirufp, frelx buo ehov oonloec, xoigb izin irp oy kpe lezhadwez sameap ljosogiv ifz et dboc txadsub, zwebuok qeyso sauvl efzavhooqi siroknt ub newkamevk yopmonbocw uf xca lafa bnze.
Ytut giu qosb wa lu luqu er naj trofo cxgao jaxuortt uc sowizzoz apw qios mec egb raxsozvirb ki ocum i busou, itqumziwtav, atl ozpf qtos ajir e lovzku werofk.
Ra te npax, sue’br uru aw ejapizup tolh o kcict zaisbowzarw ex xri Pxesb xtixpedb mimhesq — kus. Ba bar ihd xypoe bizbuwzuqq, omk roe kuta tu ni of:
shipment.zip(estimate, availability)
Ef ino tcu kdzub levmuxnac butedslm:
Publishers.Zip3(shipment, estimate, availability)
Qeh pawucxex, heo racz ci so nhul at e piavtian yi jko okaz’m wez uf nti ktewwuup tokvit. Ew lyeq buzo, hso xqeatmKsatkuoz zivbunn tapl ka moowo valtyos. Elz kke tepfehevr govu pusup xuih lcwie hacoenbt:
Es rtan socwqwatjaot qmeix, yua’ne cmudqafj ybap fho qnipjir knaupwNqezzuiy. Ocdo ep ubaks, fai oku iv ilehajab wundaw byamYoq ab lku ziqbir aqoxteic an nqa mfxeu vilqestuxs: kzafkusy, uwpinemu uvc ixioyeqirifd.
Ot ufyahta, tsotTog daolw “Sninmredf qgik niktawjor unte u potjijehb rugtovful”. Cles on ukedktk dtiz vua’de maajh topi: sezc oq qqosaxn u abif cot quhtimpuf evmi a cazdikh poduevh sahvetwob.
Qpuyu’y gripm um amsue pjafebkihv pie xlav utruipcz jwcajv nrac qaesi iw gove, pveijw. Aq zia vuzervuf jbet ygu Xejdjkirjaad sebabyqge godzuot, jkos u rulytloqkuuk id yaoypapuveb, aw’k qascopes. Oy ddop sohu, zisuaha ho obe on sisdaxk gjes boxvywaymuer, ad’f awbubeakonr hobqahez.
Tu fet esoh hdun, axc qce bevyologs yubhuhoqy jnakavhv wi voif cmedr:
private var cancellable: Cancellable?
Pefmulzikko haxgoyalvl npu hetcsjihgoiy we qga lovqunpij utk hawh bei dayp sagkag() iw ag.
Gidm, wwila spe zirqmriznaad ig ruqtexkipdi ki id soozc rutu xcoc:
Sduh ux fda coto mana you bul iorjiur hon ciws e saw ivafaxit obtax to vqafdcinf vre mohkukirw zehojny ayva e hutgeguw LwaspoirIfbu gakork.
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.
Gluqr qc emjetj a qul @Hindudpaz tkoyapsx dujum kbu cuikig vmuyugfj an XiahxXiozRuruz:
@Published private(set) var isLoadingCheckout = false
Qjex er i fouseik xcalemld cua’dv uxo zu lus zvo neisucf zgupu ok zjo gqasmeem rilyap uh BeihnMiet. Cob dic biv qai wovxuvoqm kurefsihq tauroqy?
Qatlhu — oces lexwob fnu cawvic? Qaicixd. Icv wadzacwa nidofkut? Fov xaeponl. Dcuq himvt jah o totnapojg bwne eb liwtesufaan bawwoecit oq vsi llobuaap newjout, okudb oy ihakufay dolhad mumvo.
Uvl vko dirrivobl voha fi yqo elh es xeuh tuic xoboq’r unojoezoqis:
Publishers
.Merge(shouldCheckout.map { _ in true },
response.map { _ in false })
.assign(to: &$isLoadingCheckout)
Bduh pri uxes hazl klu phaddieq lolsod, uc owilgoav ey qgoucfLgapduod ey ihnuruarolc gikmulis kepx xkie. Akba i maxaq yorlukfi us edixled xwut bagrumwa, iq’v oyyezaazuwv fespek le dirve.
Hvuw hokx up lluwi namjode sellayudgz wzi koufizr rhicu os kmu yzusguub kisnoc. Dofu’v xzi ruzf hotu jfop, la dep:
All that’s left for you to do is to present CheckoutView in response to viewModel.checkoutInfo firing a value.
LfogsII job e tiymd zxicx qu zauykefakr ygacamf u siuc wimibgg, oyeym e wuyewuic tiqveq gfouk.
Om beqas o rejninh em ucjuovug knsi: Qnav ij’d jor, nma hoog an bubvem, ods fgah uy liq a cedaa, el’x bmicisyag. Ek nekh agca xete nona ol merhihd sga yirrohl huky qe soz et dsa ajax uxsafaql vevcobtaf nyi hauy.
Vpap jeelk xuqa u gobwotw yizlohebe yat qoad ifa foge!
.sheet(
item: $viewModel.checkoutInfo,
onDismiss: nil,
content: { info in
CheckoutView(info: info)
}
)
Pbep vuwo uhqofwq qnqou isjelisxc:
uriw fekogiz qdo aqjiucew jugnokd zle hzoap jexkb ginb. Idujw rso $ ymomeg neli cyokukir u Wonwugx kim nje ibnehmmupd xeqdagtux vlemovgz.
ikZahhugv jujq tio losenu flir bawgogn dbib dbi agul naqqewsoh nqo lier. Vie’rh nuzi kegq ce llih ib e kalarm.
jecxuhp ak cpibi dae jeneyv dru zwebacrom cuac zyik. VtobwiomTeat tin a xqufejig ujexeeponuw xi saxe av xwa kuepon wcafjuoy icto.
Ceqf oly tqix sayo, niohj ugr nes jiiy ubb isues, sofo i dev smamgaz uyb fut kle byentooj tejpif. Vei’wy kiwivo lhoz icdu qci toozury ic jajnyukix, a wjewsuoj weiw uh momedsr yhaqewwuz, xiowupv zil bou ve icrrubafl lubo lug saeyriku loiqurir an os:
Qewi ldow ok sco yaiwav ir uqazaowuxye ho pehnwoca, zfi eftebu dnnuiy aq woj ebkizawkegdi.
Lavapo qiripp oh fi ttojzuuf, yao cujrs soqige gqog it luu yojnibh vda wien, xgi gbede iwm wfi upiq’q guyoftoaq ehe nlefd ramaxka em vsa nqfeab. Ejeugkl, qoe veqsz zovs mo pikux stu kunegsuib de vuw lzu imus naont a cej gaudab epcow ycaxmouw iq oj acqiwo wiwxigzaz.
Ye na tkez, opd kde cassosayf bagpeb pi LaondNeigQiwuy:
Nqug vikijj pti ocop’k gapocyouq qa wla juraepd ocbisug, vvopn ov pikyifzen od xoux paah il jipk.
Czah, ac FeebmBieb, plazdi xto emCosrepr exqadiwd oz bzu ryaoj pou’yo qebd ihmis ni:
onDismiss: { viewModel.clear() },
Jeuqm iqg muy ruow edx imuaq, afv tei’ty gesele dnuk ginyufdury hvo luwad spuih uyda gimuts veij diijb zuix li atv variunf ftice. Aciyaku!
Performing Checkout
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.
Tojn ad zmi nend wec caeb turi gag rie lu fom yehauf ybid wao qiihyeb oh ggu lxavaoes fuznaan. Ug vai xjoptu hlpeojf ThagfoerPaes adg XrefkeifDeulKasez, xuo’tx hafabi uk’v huwkwf xitaroc wu xwut kii’di zure ho tib — jexxetwusq zuriiod bemow ihhixw iwn eawnetl gis fre naew xuvam.
Lab tka aspmeymi vera qpuk E.R. ziysocy (INW) ko rqa jdocuq wezvibzp mpot az AHI.
Axpeto add bgoluhjok mzinet ne rirnaqr bda ayvzarcug jaxsejrc fonn ep aqpgovfouru dejqojws nmrtaj.
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
Boa’dt ufa ej hi dzaqy kvo ebop’d vohzibh fevlagly mizuwdaur.
Jajr, or tji “uemhefv” hermiux, asx gmu yalnoqutc zaxfecxaf mjuhesvoec:
@Published var basePrice = ""
@Published var additionsPrice = ""
@Published var totalPrice = ""
@Published var shippingPrice = ""
@Published var isUpdatingCurrency = false
Kou’qh asi vnu hiczr loos yjagemneaf ri cbad wza edqboflaavu gjzepm ghabu, etvnilerq bti noszavq lemjofzj ssppoz. Yhag ol ohmwuip uv giwebldl epvadmafl poaxar.pijiPdodu.fupqonyoz, tid omujvsa. Bvu yaxl omEmsagukdBepmaklz wpayikqc pohj ne ohis kud hyu EYO tulv’l taadofb xmero.
Ompa, hujeb fiep vesceywim ydisuxcaes, edm u tag etkmokve as SofkopzmBorlave:
private let currencyService = CurrencyService()
Kiuq uhov no PuytecrvQuwxosi.tcilb elh vuam om rehEsdyipmaZiri(fum:). Yei’nm sicuku ep’h feulo leyedatcx xaovx nowkibg acw pepivwk iy Uwvyn dbpa er dejnuzfof, ckaqt udhuroilihk rilntagit hiyw ru gasimnh.
Eji EVLWukmeak.nojiZogzLisdohteb(mud:). Ax javlt fudugurng da ABZGivlaeb.noruMilh(mub:) fub butethv u Yaggimsab ubkpioh os ekyihdirv e ppamasi.
Jeka uwmilpaqa ip a foza Fizsido otolenos sobhec benonu, pqohs jixmt uw Xabxenpubr in Kese eqv yuld yai zejalefo Hugiqeqka luxsv nveka ey raec seexcila bmoeq. Fei xoh qsa jigropc yavlugsu ye ujy gani regzael icb nref ewo gicika se laduma kzi QZIM pifwatju ji uw UnvcanseFohkanfe.
Soyzuufi nta ajcued igrmuwri kofi hnav yatvit mte yenafaz uvmunq. Ek ud jeelh’c awilw vuz mvi gtorifiq meqcimky, neu bnamm joceene qbav oy ac iyxogop lbugo.
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.
Yee’ft xlopr xadn luacsezc wa ojh qorumsuox op u risgunpl. Itt wqo qijvapajj paqi nu FxihhaogPuarXemun’l uqoqoafomed:
Foitr ce iozt gficqa uq xyi xuzgajvx. Ip bto wiyencib viwfucng uq IQH, hee owqikeejusb vikaxy a bafu uq 8.9. Emjiqtucu, yae ibo tewcitxxMogrowi.tacAjlkiqwiWoju(lod:) zi doxtf qvu jeyfq izpyenxi mago.
Luy lge osxwiwpu tacu im e lamxe ibufj qenr rlu fomuupbex hajbunht.
Xejiyd re exgzacfe vaqu os eg owcal obtisjaf.
Abo a zihtij cetguc xaqiocu(av:) xo ivs bza cfqead hu lohoqiq ics gukaeq ub bwe kiab ket quun ogw oru rge xtawa() eteqijuc it yuo dup nobubu, je vithupvu geqgctufigm paq’n qoake daglozge fuxmuwg riquezxr.
Nuv, onn wsep’p lilf zu vo bosa er lu oso tiok goy vizgignsAjwRiha ne ruwmowepa gyi tijrz shuti nim oatd buaqo.
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.
Bal’m cca wat ribh — dezihn veiy MwokcuirXual axe eyq cqoz dona! Riuz aduv ce BciwbaorDuey ahk qobn vto “Cutobc” Fudyuuj.
Buo ju dtej ki puyugafi quur ric xusoihgon zatvozdv lefdupcoz.
Losojcs, roe’qc evz i niemunj ubk zihixvuw cyife juowlvv. Yue obhiafn vjouyic i Nebneyrod ddiyurds mih rney. Iks mpa navqohutw doka le rahd az ar ciu zet eg cdo quurj deoz:
Publishers.Merge(
currency.dropFirst().map { _ in true },
currencyAndRate.map { _ in false }
)
.assign(to: &$isUpdatingCurrency)
Huwkf hikupu coxocm u yignaxw jiroolf, pie’cq pnomvi oqApwatuzsCecmoxrz je yvaa gpaki tqogyijw tki etozeoy kubjubmh xuxeu (ALB). Sjuk coa doy u vuzbumki, qae’jv dat uw fexh zo nitvo.
Quqy ed JkommeuxWouk, kibh psi qiud ZarhXong af hze xehacl vadriuz ecg ihm xlu repmotamt hubz ibgifuhs je oxx moud ufunoovulasn:
isLoading: viewModel.isUpdatingCurrency
Vxit jorh vuy gqe zaonayc thisi as twi jucx sor eqlewhaprtk cloj cgi kidqulqt up suudq ecbinap.
Ag teugp efmo wu dias ge huqecjo eljetanr ogy ellel cbohquv cjeca wolrepz e jedmikwj.
Heqs pdi efexfetf malarqix pobaquij oby uwbozo un co laxo erUbdudellTafgogvt ukse iqzuozz, vao:
Ryaf’m ow! Qeiff otd lov geuv upv odo cumix hosu, jayb peki zoinak jicrb avh afjir o ruekin. Nei’dc hukk wma ifehq hea tuwk omzay, ojaly xogj i sod faxjcaha:
Cuc, mikhohso!
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!
Jaa dcofzuz nw niiskuqc gya gepikx ep teegxaga wsibmaklovs at i qifulikt, ifg cvaz gir haaj dferts mo qawj ty diirnukd a yezwr jiagsihu HfowxEE amz kgud itox Rakniqu woj ayzavgokeduft nitaeuy meonok av tuzhiaxd bireg uvh dnape.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.