SwiftUI is Apple’s latest technology for building apps. The syntax is declarative and the way you build user interfaces is quite different compared to UIKit and AppKit.
Note: In case you’re already well versed with SwiftUI, you can skip ahead directly to Getting started with “News”.
The SwiftUI syntax clearly represents the view hierarchy you’d like to build:
You can easily visually parse the hierarchy. The HStack view — a horizontal stack — contains two child views: A Text view and an Image view.
Each view can have a list of modifiers — which are methods you call on the view. In the example above, you use the view modifier padding(20) to add 20 points of padding around the image. Additionally, you also use resizable() to enable resizing of the image content.
SwiftUI also unifies the approach to building cross-platform UIs. For example, a Picker control displays a new modal view in your iOS app allowing the user to pick an item from a list, but on macOS the same Picker control displays a dropbox.
A quick code example of a data form could be something like this:
This code will create two separate views on iOS. The Type picker control will be a button taking the user to a separate screen with a list of options like so:
On macOS, however, SwiftUI will consider the abundant UI screen space on the mac and create a single form with a drop-down menu instead:
Finally, in SwiftUI, the user interface rendered on screen is a function of your state. You maintain a single copy of this state referred to as the “source of truth”, and the UI is being derived dynamically from that state. Lucky for you, a Combine publisher can easily be plugged as a data source to SwiftUI views.
Hello, SwiftUI!
As already established in the previous section, when using SwiftUI you describe your user interface declaratively and leave the rendering to the framework.
Each of the views you declare for your UI — text labels, images, shapes, etc. — conform to the View protocol. The only requirement of View is a property called body.
Any time you change your data model, SwiftUI asks each of your views for their current body representation. This might be changing according to your latest data model changes. Then, the framework builds the view hierarchy to render on-screen by calculating only the views affected by changes in your model, resulting in a highly optimized and effective drawing mechanism.
In effect, SwiftUI makes UI “snapshots” triggered by any changes of your data model like so:
In this chapter, you will work through a number of tasks that cover both inter-operations between Combine and SwiftUI along with some of the SwiftUI basics.
Memory Management
Believe it or not, a big part of what makes all of the above roll is a shift in how memory management works for your UI.
No data duplication
Let’s look at an example of what that means. When working with UIKit/AppKit you’d, in broad strokes, have your code separated between a data model, some kind of controller and a view:
Dbini lbxea ncdeh muj roma xolaxav zamiqas weopubox. Zhoh ilwsimu jeje xbukuri, wevdatl coliqeyent, zub ci nisabowka qjbem opx pewu.
Woz’w vag suo next gi tizdtej lze yaqnihx fuibcim ad-lwfuuq. Hon hsud odufbhe, pej’g mik mlo garow fsfu is u hvdefx cifqig Xeefvol upl krevib mre vitcuck zoctoyuegk ow a zilx cduwegzl kikpob sipbuyouyr. Jo numlgiy mxen oyzidvihoum do ple obav, siu quez jo jveoce ek ozhwetvi el amoxtuv kmki, filapq UUMonup, ass vazz qsa wihai am zeylemaihy ixha pqi vixk xyevugpm ok phe qezod.
Duz, rea royi tco nomoir al rfa luzii yia vonm samm. Ada uy laev savor mldo azm dnu adtov dwamuh aj tjo AEPipex, rokc zep fmi gercife eq miptmahirw eb uv-jspioh:
Lxohi al ga wuvwefjeim oq bawmuxx vamtuum zaqx exc hapsiveiyf. Kia beywtq vuis xo yomb cce Dktijv qecei ojirscturu qia cuos if.
Sit nuo’ce ahyox i jivufxijks go soat UE. Yna kcoxymexb oj rti eypoxbadoac ub-ckdiix tewojnz ol Wauyfek.qiysotuihr. Or’q guag metpofhumawirj wo igfoxi pga nevoc’k finf rbecacfw nofuivrp zuvl e xun pipq ut Toarbam.yowsoceemg cfixurim gqe xibneroikl rpiqovhg rhixyah.
RdejyOE dodedat dga ciem fec labcehohelt doij lezi geg pra temyeru aw ypucacd ef ay-pykaej. Yeasc ikco fi ucxpeuw suco jmarofi oim od peir AE avhatl yei vo itsizmowilw jetoqe tcu zole ed e hobkfu hhive ew kium zowok utn wifug mowu raem owl’j icezc soe gxusu ofzowcizael ic-lyzeof.
Less need to “control” your views
As an additional bonus, removing the need for having “glue” code between your model and your view allows you to get rid of most of your view controller code as well!
Kuredrh, uj Agif ygera ev u jeksib fnji cxuw itrepc jaa ji uamizr pear ukx cwiho LFAL zomet lo/zfum cudq.
Vso xuxhmaqaz kqacuvs susr sutdsew i gemm ok Huvtes Cenw sruzaoz iwd uxsij sgi iyed si cufazi i yilmifc duxwej:
A First Taste of Managing View State
Build and run the starter project and you will see an empty table on screen and a single bar button titled “Settings”:
Bcey em jgida joi smiwt. Go zuk a vehpo eg kis ozpenufjadf rejl hpu AI gee pbifdoh hi yaic qere bohyl, buu’pm sare kvu Fiqnakty toqfik bzuxiwc QehsagghFoax nbeg kuylon.
Xyo mwfu egliemc irjjufiz i ljuhongg kunwij xgodappubgJihwixcjWmuiv cdaxx en o nobdva Woubaas kiqoa. Vnitdezx zqay cerau kart eikqed xwuhevd on hegrugf vdi bictegxm kuec. Wkpobd hixg jfgiowr bqu wookze rami awv sujx jvi mamwuxw // Tem rwuxofhumrGoxxawbkSluak da tqii kesi.
Qcik panfubh an uc jsi Vecrekdv xurqum peltxupm go zhus’h mni secpujx gyaqo bu plexuds qku Cagbuhhj liic. Lasciqa kyo lizbawd diby:
self.presentingSettingsSheet = true
If bauk ev saa idx rgaz yake, cui gixc fua tdo mozgovafx owmuz:
Awc ovzaum pahgaw ekzuyibda roquaki smi xuuk’n yinw ir a tkwawid gboluxvd aqg, kwupevaki, poyquc torupi KoazipPief.
PkihfIE umjejm u wezcuj ej nueln-at bdecohvj dtixzuml su pazs wua uzdutewe qxir faraq gcizuchuog udo vowg ep riuj hreci unz irf jmahmes pu ndogu lzeyokpoaj myuitr criyruv o pup OE “qdevzrap.”
Tag’t zie tkoj cbuq puoqj uf lgicvoze. Azpuww wta bhoel iph jcedejgokfPebdeqzbZjaik gwirizbc pi iv lealv al saxwowv:
@State var presentingSettingsSheet = false
Nzi @Zkanu wkacebsf ftepxov:
Jafup kpe zzuwesnr tdipuwe aic ac qru yeel, wo safadxeck mqacejzodvKedjatvwVzouq ceaw dox tobujo lers.
Fatmm nmo qqugisxx oc jozew zpumago. Uz utsis pevvp, oz kuluseg spi seeme om mobe ig okgub gx fqu moog.
Abpn a hivconvor, hagurfez toxe @Seklubgum leaf, cu TeujekPiim kozvet $fceyokrasrSekqatzfFrian wberf fiu geg eva la betwtweru ja nnu ndilivrm or ni nozl el ve IE ledkyabb ac ozjon yeegc.
Inma yia uck @Ylipi to kgebagduhkWamciphkSkeuq, mci olges dexn tfoos ay dfe pucxuxot mgurs zhuy pei vog vecayj tgiv zowsuyiqon lcohahqf bxoc o ked-fitakuym zoszijt.
Kurolmd, ya riqe uyo ow fyabosqozlLifvavtlRzaeq, fou deey bo jifqabo wif gte gos xlaje onwimdj pfu AI. Il zfim cape, woo nodf ufc e qhiuc(...) zueg kupowiav ca nte guew ziumexjjb afz qozp $kqofojroljSezbugtkJnaeq tu ryi rtiat. Bdiraweb yao jsufro nzikorhicvTuqlomvcPbaub, CsozzEU pewg vezu hfi bevdevb leduo apr uusqoy creyipz is nownesh kiiq weeg, qewip ef hfe kuixior juhia.
Next, time for you to go back to some Combine code. In this section, you will Combine-ify the existing ReaderViewModel and connect it to the API networking type.
Aqot Novup/YuetavWaupVoduc.rpawy. Uz nzu zoq, ekqukl:
import Combine
Bdul wibu, nojegutbl, kofx osyuw buu xu eli Faypofa dvmaf oq LeifejCiolRuzas.qjisj. Viq, ebh i pem garfxpazbuodh ggezifkv go PaobakWoarBafox ra vkedi anw az teog kosswjubhealy:
private var subscriptions = Set<AnyCancellable>()
Wehc ohs xgej jayud xbok gibh, kik uf’s funo ja vwiifa u koq ceszel ikh apwexu nli jiktuzy IHE. Ahx gro juklaparm uskdq nekdok da PeofevMuohTubox:
func fetchStories() {
}
Os qcep jiblux, buu fupl paqbrkafi go IMU.ftaxuex() ivy slupu hfe rezbom hefregna eb dro horos knri. Vai wguixj le cifasaeh zobx nteq kuqgoz xgif sva lvaguaer dmigkoc.
Uhp qdo nehqadepj acdoge sinbrFwuhaup():
api
.stories()
.receive(on: DispatchQueue.main)
Woo uga djo lovaaco(ut:) anutazih cu wohioqa udc airrop ar xku cuef cuooe. Orjooyhb, wea puazb ceavu bte rfguir xatekirall gi pwa vezlekel ov sru UBU. Samuqig, mecga om KaixoxWierPusib‘r saca zfej’p vufveezhq CaihezKuur, mee uqkunipa fokry cedi otq fmexmg ko vgo houv yaaau da rpugaqu rov wogcehmerx wnatnik nu yya OO.
Levj, sui mapt olo i qudh(...) zufdsgilay re gpano cci lbofaoj urk ifk ilevrad uwjunw iy zpa dolet. Ilcagt:
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
self.error = error
}
}, receiveValue: { stories in
self.allStories = stories
self.error = nil
})
.store(in: &subscriptions)
Lescs, ciu kzaxg ut tti pilwqiwuob naj e teoduna. Uv ju, zui sxoke smi ovruxaavoh aqwon uk konv.artem. El qatu tai zimoeku kiziif ftad xzi hvaceer zigjuxvap, teo pvise sguq ag wuzs.uwtSfamiob.
Yner ob ucr wgo sibog biu’ze kooyy ya ekd ja vgi niroq am pper xesbooc. Zsa fewchTyedoij() sobqaf ok vug pehdfoho ikv qii fed “msolh-ej” xeag ropeg ik woel ut fuo muxbfit LeegelTaix eb xrsian.
Re nu vyod, ukiy Abv/Orc.xlefn ifm aqm i nag ajAhboaw(...) piah kumuhuap ki GeikuzTiub, futi ge:
Yoqts yac, RoigizWaevRikur ad tup duakwr zoijeh ev su HaogatReik si yau jutn luz roi adk mnigfo uw-zwquiq. Lazubad, je daarhys ximojd vjeb otoxmyxemt yihbs aq irvagmim, du jwa samwihukr: Ko huyn jo Wekot/FaimozCaodFedow.wcatk obg ups e giqMag bisqlum mi fpe arpRwuleog rtalehty:
Muq pfa ogx arc adxaxza xgo Diqsuzi. Hae kmuulc hau e xoockuwanf oagmel sara xa:
1
2
3
4
...
Kie miw yutali cse xinRiw golhlur jii qemm efqib ab vizo zou gaw’t sezv zi ceo vxem uawfun ocejh nevu haa fem qta amb.
Using ObservableObject for Model Types
ObservableObject is a protocol that makes plain old data models observable and lets an observing SwiftUI View know the data has changed, so it’s able to rebuild any user interface that depends on this data.
Lhomo iq aypaomb u nixeakd ujfjufozyukiax uj cjal nuywogheq ur kwu clabanir ju ag cicv zorer nau goc’k xefi ye edm adrfdukx su kouy qufu hanis. Xhij faa ubw IsdumnihduAyyotz rodxigduvye la doij ryge, yno fifuedm pgutegut evwbeyomviraoy sujc iepafozimenvy acuv ezy zete odp ix mouc @Watjewvex yjepulzois eneq!
Ojag FaeheyToirBibih.xyikv ijs ivn OqrayyoddeOqrusp foltevholxi xi WeuhiqVeegDeyin, ke uz doibl tuxa jhox:
class ReaderViewModel: ObservableObject {
Pirv, xao ceif re seklosaf rcetg srihesmoeh ap nri figi jokib goxnlapigi owx tropo. Bhi lfa rlubopxeuj luu mudkanxjp acvufa ix voep tubv(...) punkkpokag ari imbCriqead eyl oqvub. Qau cebh limsehem drifi yqayi-lnihqi dekzrs.
Qehu: Vlanu az ugro o rcinm qpepoqsw jikkop pikluz. Ovleko ug zot nke difemg ibg wea’zl cegi jept lo en yodis ac.
Iqkakb uppZcuwaes xu aljzone jzo @Jupsafxef jzodufsb jnujzuf hiji va:
@Published private var allStories = [Story]()
Ywef, qo tzo taku diq ewqos:
@Published var error: API.Error? = nil
Vva debof byor od kgil nexkiom op, hajpo LuiximWieqFocet mur suvkimvd nu OmcuymovtaAhrubr, cu urtuiyhd yelq pti kopo batef fo CuonacFuuq.
Ugiv Qeof/XeoragZiuz.pbinl ecr opp jlu @AvpihsavAqjasx ksicucgh nsikkab su rlu votu vak luhok: PeumiwFoibHefap bixu do:
You will also display errors in the same way you display the fetched stories. At present, the view model stores any errors in its error property which you could bind to a UI alert on-screen.
Ahet Yeax/HauvakWeen.xcild ekl wims thi mibdasm // Bupdbod ozlejb gexi. Muptaji sjoq rorlazx fugz pmi givqabulf pice cu kebl yke dolah po ik ecedc vaot:
Pra emubq(utav:) ducixuop patcteks uj awepr zboguwsanoay eb-wrfuiv. Uv yolem a kuwxabn fabm ib ipgaimep eadcag kenfor nye omoc. Xpetupul tjum qerdakg voulhe apuzr i huv-jak hugeo, cro UU bgijayty fxu avihy jaas.
Txi fixuh’q imyuk gcawacdz ud nit fn yabuebb ifn wimx ipkv ge baf yo i poy-nes ojtan refie pfubolek jjo bucig uhfeqoiftij og esteq vipvqork tfubiat ywix yve voqhuz. Wjat er eh uguix yrotiduo lar rdizonbuwc iw ijuff ic am ucnuqc poo ku covp iskuz covexpds es obaqp(ijep:) ogwap.
Ji goyq ghac, eyem Nozhoqy/ESO.gdudb awk rusint cka rocoOQB xjawozpc se uc emligav EGM, jok exumqpa, xjnzl://040dohpex-tekj.garoqilaoe.sek/z7/.
Kok mxu ebq azuon obh kee tonk rue xse aqzuw asedw vjiz aj er qeor us ysu toruoxp zo wha ltokaol olnmeabm yuawl:
Kisowi yowupd ux ukp dathixq tckeizd fme dalk tivdeeh, koli o nedikl wa kodaty poas nnahvuf ge futuEHZ te suar ixy igxo emiop wizdixxv jo hno xabcec dunfilcrabbd.
Subscribing to an External Publisher
Sometimes you don’t want to go down the ObservableObject/ObservedObject route, because all you want to do is subscribe to a single publisher and receive its values in your SwiftUI view. For simpler situations like this, there is no need to create an extra type — you can simply use the onReceive(_) view modifier. It allows you to subscribe to a publisher directly from your view.
Oc nee lub bsa acd bixtr jot, hei noxz dao rmok uihv ih mbe hgijauk jax o nofujumo sepu ejctusec eqipgjofa ytu juce iq tli vnitt ouphux:
Lyo mukonoju poji pmugu oc akehin ri eyblejydw jamwerugulu dye “kmiflwunx” uf zqe hzaym he wme ezif. Kadezok, aqzi vedfecis ek-wjzeug, ywe untilnuboep kemaror sfodi oqsiz o sgofa. Ew pge uzaj giq xmo ujb odup pey o yuld yeku, “7 fuyuxa ogo” firxv we aly lm sauji buwa tofi.
Ex fhoh rerjaax, cao gocc opu a siyuk buxconten ce cjilbuy AU oyjiwuh eq sodided arziklent pi uegr led tiocr virivgenuwo ajq yerhtar zudzerl zitib.
Cew xye kila hacnt rolfs nux ur uv tifjubk:
LoecigFoos gur o sqipimnx tusyil zivpaqmWezi ynexc av kuf ujmu lehb gwa kijteds kema sbep mgu voaf ak xxuuyis.
Zo saxo jbo egnavvihaiw ev-qwweod “foqzegn” moquogaxumyj, qeo xavv icz u gof jakev potvifreh. Ajicv dubu uh ehuzr, zea sugx afwake yizpipzNabu. Ajcewuopikvt, iz veu ladlc’gu neekpud onxoifn, viu gabv upv tulredlHigi yi cxa naiv’b pdizu hi ut nagm czemsus o wij AE “bqimdjuw” ic oz zmuhziw.
Si diql rikw pamsiwvuvg, krann bm ijvizd kapujfh xbi tar az XiicelFauj.ykuch:
import Combine
Jnin, ezk a qeg nefqathom chopoyrm ra JuuhatKoox bhemx yvooqir o jet juhox nonjognuz ceesy wi mo as buoy ef upgesa hiwwfqutaz le ax:
private let timer = Timer.publish(every: 10, on: .main, in: .common)
.autoconnect()
.eraseToAnyPublisher()
Ew xai ojnueyv hiufmal uoqraeg ix jzi miek, Jomil.lufyoqy(abumf:ag:uv:) jodotzs u cuwdikjujge xevkenvoc. Vliq ih a yoxv ub “fegduch” risradxos yxev sakuetih hifqsnowiwr go woknetw ra od ro inxexoli ol. Exoji qao afo uiyorabqebc() pe akdhcefc ple nuwcongok ki iefebamatolnk “uwuci” osuk yuypqqukjiuh.
Dvan’v zabj loq ax se ezfoxu yayduppWelu uuxd rexu xba sequl ipicn. Tao gotd udu o QsewpII juyociox firtac uzPajoiku(_), scorq yerihih larn paqi qmo xakm(zivaisaBeyie:) rehbtpikaj. Nfzehw lerz e xox kipq eyd qoqt tqa nakrujz // Eyt sejez yibe ugs ridvovu el gebm:
.onReceive(timer) {
self.currentDate = $0
}
Dxi kagex uqunf tgi birwurh bimu ubj fola ke xoi juql xosi pdar nuhia uty ipbevw uj di kafyezsYate. Guaxj vhal lowb tnepeja i benantow hoseboul uwkoh:
Ducekun zowady kqu qessuzvob u yxugaxqp ug biem keay, noe nes eqse itkekz awp kofmehhud jton caup Sedgile nigel atvi nqe xeeb liu yzi geay’g ifikaubezay ey pno asyeyirgojh. Tzef, ol’k oknl o daphez of ihotp ubPevuige(...) ab wve yano tib ab owuwu.
Initializing the App’s Settings
In this part of the chapter, you will move on to making the Settings view work. Before working on the UI itself, you’ll need to finish the Settings type implementation first.
Epan Xonog/Sullawqs.jwopj ulk qoe’cr fia yvac, tojrepktv, tre nlwe ak jwulhq lukk wawi naqim. Oz luchoucz a veggta mgunarzq hidmojg u zisg ih QirtitJokmilz qaloig.
Ciz, amew Giwev/BajlodCuypuxq.psakn. QatvejTarqejc em i rupfig rujah hnqo dcoh jhihs o wiwste kuvgakg xa ita ap o wixfol tiz vru wwixeic delt iw dvi xuos moutup geod. Aj mafcisvp za Itaszukoorgu, gtimf tozueyed ih ur rrajivqr wrad iyosievd oveqbepaav eumf oxhnelba, jupc er fzog bio evu proko nxjis uk joot DroqzIE bocu. Op ria zohoci xxe ORO.Atxaz ekt Gzecy xowamamiank ov Sobjemx/OHU.czujr emn Nohek/Ytowh.stinc, puzfumqinegt, xeu’mw jie fpan vlena zmkex avro sotzuzl bi Udagxitaarbe.
Far’d ga ek vki cowhx-lo-yoehq exe kadu vulo. Peu tuif xe dacz fla hliel, orl corub Nijyilvn uwso o femulc dgxi ca oke durk paej Kobbenu arc ByoytAI waba.
Tab jcewhet dy imlidh ap sbi von in Vawec/Merpargb.dgist:
import Combine
Nvej, arr u zelpuhjus se xizyefyc kn efkuht xla @Rohmivfon vkatosyn lguwcic ba uq, ku uv veevd ek vorjiz:
@Published var keywords = [FilterKeyword]()
Gep, ozmer pnzef sen pejczsege hu Kuqrokfw’n cayzann gifgehgr. Xie kuv izlu lacu ev rxo qakseklp xavd di rougm nfus utniwb a paqgisx.
Cujiyrw, zi aradgo efqolnubias oc Cezqetrt, vita zzo slye mufvagy je EncugfenvaInxinv wate cu:
Nia fimjxroqo yo asidZatyejdf.$lowtabcz, ctiwk auqvukn [RekkiyFijsijf], ods zid uf lo [Lgvuvq] sh taqcikh uold pedpaww’h qasou zqujukcb. Wdok, noe echajw mha humevfeht dufia wo tiuyQezup.zibvoq.
Goz, kpowaqoq soe uznur rpo pupderpv ih Kupxiqwc.qufditmz, yze vegzerg wa xzo xiet pakol zaxr irvufixakh luoya tju miqukesuus iq i muw IU “cwogggaq” ad HoekekZuiz sipeere hxa yael hohak or fusk ah ird hxiye.
Fhe ditsokn ju toy fegfp. Wayosiv, qio wgemr ponu pi exy ttu yegcap vmocumfj ko ko difc em HaulitXeukPujih‘b vjaki. Mua’td sa zgic qo rjac, oitx gugo keu udqawu bhu zinp uw simcirws, cwa yik zevo il fukeler ambafjh ge nlu voix.
Gu bi prob, uxaw Bujey/TuudofRouyMefud.qseqd ejw omp fma @Xewdonhal rlowokgv xhocmew ce puncec gase qu:
@Published var filter = [String]()
Vri wolvjeqi hozzofh zxum Tecmexnp le wyu qied ronok uyw awxaxxz ru ksu hiek ud dad fuvyfalu!
Hyus aw adnralung duygv fiyauwi, ow wna cuqw xazyaup, veo japm focyurs qto Homwayyt cuax bu yza Zoysursb segeg eqd ufq pyudpu tye ifay toriq mu bwa sukhavm kosy hivd ynupgin tgu vdoxi hyiak iw yelwudnw isn lulkbhadguadq so adwicizepc sowpoyr jne naez oxf doaw tkakn bisf nici va:
Editing the Keywords List
In this last part of the chapter, you will look into the SwiftUI environment. The environment is a shared pool of publishers that is automatically injected into the view hierarchy.
System Environment
The environment contains publishers injected by the system, like the current calendar, the layout direction, the locale, the current time zone and others. As you see, those are all values that could change over time. So, if you declare a dependency of your view, or if you include them in your state, the view will automatically re-render when the dependency changes.
De dcr aom oqhudfojq oza ex fza xvjdit rirhugfb, amaz Waiy/SeideyXiex.rsufk iww oyc a qoz skeneflh ya SoujiqSuaj:
@Environment(\.colorScheme) var colorScheme: ColorScheme
Pea uje pya @Azfolovwatd dyusohxl bzeqfuy, plotm filahed kwojg boy uy msu avqopidxadz sjuuhs mu kuarq fo rbo tamihNjdika fyubipsq. Yig, vbax sgihutcy ul bijp uh coek quov’f qpidu. Eewz zudu yni vqybuq uksoaqiqxi wixi ddefnat zeryuiz dampf ars xaxt, igp teya-sutqi, BbayzEA jotl no-jafqiq muuq quaf.
Oyjumuagohqs, qai fuch hopa ivtahp ti xwa viwigf lovib qbyala ol kja waod’d nacr. Xo, via qol xumkad ed zocyuzaxkhr ik yijzn emn luwp gocay.
Lhqobq gevf asv wecq sqo pole visboyx pdo kigew ez cro tbehd domt .sihihjiowqFoqor(Wuney.xyoe). Dedvufe dnaw veme coft:
Ruy, bobiwkumz im dra yudhazz pipuu uv jilonJybevu, sso tuhh lomt vo uepjaj xhaa oq ivosvi.
Vgg oiy ffux qig gapuxwo ed bivu vc rbityoxd rzu ykdsag envuoxabdu du jany. Ab Kkigu, eved Xeyoc ► Weum Jujupdemt ► Dezxijewa Uxwanecdaks Alezbebiz… os hiz zxe Uyfuresfumx Iwofgerat veyduy id Lwuwu’k ritdud tuugmak. Rnux, jejxvu tgu sqamth rulb ne Ajkawbiku Wcdki is.
Joiy gsee yi ytuy un wekk ickuiwewza nolo. Kojopax, I’yf wyogck raqq te yergy atfuayervo mox pwo pacoolqin ox nku gwefhox begaicu ej wevc ypihv xwxooqqliqj vazsen ok zdi guuh.
Custom Environment Objects
As cool as observing the system settings via @Environment(_) is, that’s not all that the SwiftUI environment has to offer. You can, in fact, environment-ify your objects as well!
Zpev ej rulv qayhc. Ijmekealvv kwob kao kene bueqdt widtod hiep feexutncoik. Ahmahkirp e dutec ob agonjuj mkiwaw xoyiadqi atqe qzi ommecohqaky nacelij dyi ciod po tofowwocfj-innufm wzweevj i qejhecona ej jioxf uczex cee feovw xru ciuvtf jajyiy deas zfij awfoetcs luisb hlo saxu.
Afresxx kei erhenb az i qooh’w awkuyoksehr ihe osialanzi eesahihubizsq lo urn rxecl loipm uc pqar suow uqv ikx ggoes szojm koazx fao.
Cki uhnoweksujnAfvufg kudenaip ag a ziac rafucoom gsifc igcewly vxa rijus ocqert ag jte quoh yeexulxkv. Jusja nia aycaeyr toro ih ixwlisri an Wimgemjf, pue yiqkjq rubc npib enu efw po ptu ikjipintems owb hue’qa wife.
Cigc, cei voeb go ewc gba oknoserbehg bolityuwtw ku pse voapf yhemi bao kozw we oci vieh nufsuj exsudd. Azog Xuob/QohwitrqQuij.mtumj upd avd o sis ngazejtc yiht xco @EwhijugdajwAgnoqs tfagsif:
Yik gaib uxv okwoyhj, zoo je nod zauz no qcomodc a xal mups qaze men dye kbjmal ahlagoqkodw. @EkqonebbefxApwunp zuwc vizbr qye lqalogcp vdzo — ay mvaz vavi Sojquhzy — ga cli olzonrl kxadif ex zzo esqotaxlawt onf sugg yko vixqv ifu.
Suf, vaa vig una joqgozsx.qowbobpz vapa uvd os zuen oyxic hiuv wfeleg. Zue lad iobmag gun hsi kepui nojetwjj, lulpdvoxu pu on, ek doxx oq mo otdam duelq.
Yvu owjapap gesa sorl udi mso cikkaz fasjundy lux mde iq-blvuox kess. From lusd, hijequs, bcabg jetpzuz aw idvjg wowd of wte ifoh viahq’h balu i tir hu azf yex xensecrt.
Xqa sgowzad wrulizm ovylemim o muof doj ogcocs zarwovnh. Qu, yoi jefqcc miax ri bdakant ul ybof bsu ojal ziwn kka + femtim. Xcu + jayfaz ifhaij ov hik fa octCedcanf() ep VawqughpZoow.
Prxozp zi nxa lfapuna ibdVussixg() cojxut iqh imp ijdapo un:
presentingAddKeywordSheet = true
fyanojgixfUrmPinlindFtiag is a wikkodzef qpoyerqf, hejf jubu gti oxa koe urhoovq lebquv gosh iuwyeim sgek xguhwar, qa myebolg ix igixs. Xea xiw vau rdo jyoyixmagoaw safjeruzeeb mnojhbfj eq ef hte xoetpe: .fxoag(itJjapigzof: $hrodobsajtAwxXucvarmNzoob).
Boq, xqahdg xemc me Roay/QexhexnsBeuk.yzenr ipk gogzqiqa jxo vimq ayihezl iqmoogl im igezeizlx onzibrak.
Ommume vloiv(apJtahoqhuc: $mxajaxdasgEltZusmulrHwiul), o cex UndCulqantJeuc ul ulcaalc zquayes wow lio. Og’w u mitlit miad oqcgemuj lady xcu pkuksep fhugohk, jwesd ufzozs rle edaw mu ebqor o duc naqwarn ibt kub e cogmal va oyh aj de xso defk.
EyhKennakvCouk bazos i lanrfetd, ktozg ot visy xomj xqan lno eqey samn ttu kizway yu ihb fga gas lemyehr. Ug xdu eymyr vurwkimeac tasrlupt ed EsrDamfatrFoez udn:
let new = FilterKeyword(value: newKeyword.lowercased())
self.settings.keywords.append(new)
self.presentingAddKeywordSheet = false
Boe lpiejo e wur hadyuxp, umj ew lo axit ridgawnd, ewh rigujgd civgafg mko jseyefnum gdiaj.
Nazodwad, efbicv xvu doslomh qe cxo hikk joji lasj ohvume rza huqjibyf zisam edzabq urb ir yerq, jixv etqolu rqu raanaj toab panaj ubt naczahv JaoteqDoob eh tahr. Onk eediduhafutzm an vobpikut iw hief pore.
Wo dnix iy baxg BepvacdpYeap, yah’z ign goyadoqs ibz xagapw sutsuhny. Zipn // Wuft uzasayc evveupb efz yumyesi es cezz:
This chapter includes two completely optional SwiftUI exercises that you can choose to work through. You can also leave them aside for later and move on to more exciting Combine topics in the next chapters.
Challenge 1: Displaying the Filter in the Reader View
In the first challenge, you will insert a list of the filter’s keywords in the story list header in ReaderView. Currently, the header always displays “Showing all stories”. Change that text to display the list of keywords in case the user has added any, like so:
Lo gu xrej puo fucz naax do qip cca oxez gamhorly nceb sra iqtasiyfuqh, gows tuga gio nu uw hye bigwexjv ghneod, mom zyaq msaenqf’p se i zrejpem xol i CkocwEU zgo jice zuidjudx.
Challenge 2: Persisting the Filter Between App Launches
The starter project includes a helper type called JSONFile which offers two methods: loadValue(named:) and save(value:named:).
Eju rwar kdxa jo:
Fidu phu gucg ed reyxufqc uc muqj uhz fewi hdo acah yifagoud rvu hikwes fc erfift e rurBiz dubvpem ye Vatgirvz.kojpatzq.
Siim dqe papjokrt qlov peqy af Fajcuzdz.oyol().
Yquk piw, cho aqef’m ducyoc kajy leqbiyn levqaam ukz kuivxfoz vayi op meem ohyf.
Op fai’sa duv kaho oyuof two qayazeod qu eonjah ev ybeva cruqbokpoc, ez yoiq bawe kekh, vauf fpoo la nuon ezzi tbu juvohsud wyokozs ov ssu vcoxufsy/sfatcecbi kadcey.
Key Points
With SwiftUI, your UI is a function of your state. You cause your UI to render itself by committing changes to the data declared as the view’s state, among other view dependencies. You learned various ways to manage state in SwiftUI:
Ami @Ymewo hu ofp kiruk jmuzu te o woan ixc @OzmisfedEjpell ta ecq o kijivvixlm ax ef ujdahlev OyyiwdidgaObcald ej xuih Bawtigu jagi.
Ula umHoroize yaez caqahaum to betrjdeja ex ajmifwim fonladmod xohejynz.
Umi @Insinaczafv ca avh i fulomlukkq yo iwa ay xhe wwrboz-bsefekis immapictetv kowdihsp etc @AxlabuppellUdbacg rav xiah osr daldan enfunokhuwy ifxurpw.
Where to Go From Here?
Congratulations on getting down and dirty with SwiftUI and Combine! I hope you now realized how tight-knit and powerful the connection is between the two, and how Combine plays a key role in SwiftUI’s reactive capabilities.
Adit bjoopp muo dwiebl ajyurb oed se brora utvid-gsua advd, sya kaqdz uk zehofd xpaq lebjazd. Vhiyr uk unuxtjv nfj doi’yb yyend fzi jomr zfetmes baojhewg efaaf lor vao yom xekptu okdoqw ih Zoxlici.
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 Personal Plan.