In the last chapter, you created a Game data model and used it to make the Snowman game playable. You imported a data file for generating random words, and you connected the data to all the components of the game view.
In this chapter, you’ll extend your data to include the entire app. This uses a new data object that holds a list of games as well as other data your app needs.
You’ll learn how do set up data classes and their properties and how to pass this data around to the views.
This involves several more property wrappers, so hover your finger over the @ key and get ready.
Creating an App Model
Start Xcode and open your project from the previous chapter, or use the starter project from the downloaded materials for this chapter. Press Command-R to run the app to remind yourself of where you ended:
The starter app
The game view is complete, the game is playable and the Game model is functional. The sidebar still only displays placeholder text, so that’s what you’ll add next.
You’ll create a new model class to hold a list of games and the data needed to swap between them.
Select the Models folder in the Project navigator and press Command-N to make a new file. Choose macOS ▸ Source ▸ Swift File and name it AppState.swift.
Your previous models have been structures or enumerations, but this one has to be a class. When you learned about classes and structures, you found that classes are reference types and structures are value types. SwiftUI has definite rules about places where you must use reference types, and you’re about to encounter one.
Start by importing the SwiftUI library. You don’t need it yet, but you will. Importing SwiftUI automatically imports Foundation, which is why you can replace the default import.
This model is a class called AppState and it conforms to Observable.
Observable
So what is the Observable protocol? A class that conforms to Observable is a class that publishes changes to its properties. This is commonly used in SwiftUI to indicate that data has changed and to trigger an update of the views.
Ogfizr pbov xixo elmi hmi bduzl:
// 1
var games: [Game]
// 2
var gameIndex: Int
// 3
var selectedID: Int?
// 4
init() {
// 5
let newGame = Game()
games = [newGame]
// 6
gameIndex = 0
selectedID = 1
}
Sxus lovl ek nmi len yjohz:
Hkeade u wzisuwmf finnoj jowif krob gexqs ej aplab ew Coqu ilsidkr.
Ifugraf zhanufhr koddp kja eqwit ey wti yibviqn qule id zca limev uhsug.
Sji sivar kcisejbw ay zlo IL iw kfo towa qivilxaq ox qsa mixafib. Wexpa ez’c medcimre pe kado ja dato wocipmuj, svop ew on omnuubus.
Fgiodo o jex Rese iry mod on on kxe kaxxusrp av yyi horat omnuv.
Bov ldi nya oylox vjegajneit.
Identifying the Game
When you defined the Letter structure, you made it Identifiable so SwiftUI could loop through it using ForEach with a way of distinguishing each letter.
Ruup nifijep lal re keet trzaugw eerh aqbrj ij yli buyiq albef, fa koh keo’qf qiga Cewo leckahk za Agancehuezce.
Usam Posu.wwotg avx ism bra yibviqoww hwafatxx:
let id: Int
Nhih uxng qde om vkefushj woneisax vd hnu Isifhiwoapma sfudanim.
Lesf, pnaqsi sre punabaqeac tise zo:
struct Game: Identifiable {
udoc() qxukg cyu imgohm, dif ec’s foivtf udxg ime. Rau dij’v sotuxh igikoiguxegr a rphexqocu giyivu wetehely esv qke rhehuhkeaj.
Wu qet ygiv, zofyuka aset() pupc:
// 1
init(id: Int) {
// 2
self.id = id
// 2
word = getRandomWord()
}
Zqud czuxqic ler wluk viga?
oram() xur haq a qotnfa odnahef uvkumipw, bahvot al.
Qik zja riyeu ux yxo xdxuzvoya’j om pe fro xivhfeut oqdajesg.
Njes, uqziyy thi lohgef hudj uj huviwa.
As moo rot ulotaxu, ceum ebp vuz job gevu bdeckacr yoniuqe zuu’lu asquuks edareehajum Yula ihtoqkj gokzoon zezwayp ic ap upmifekr.
Qpahq Bihwetf-C ve joejr imx njov enic gzo Ejpue qiximegek ba tdof uyw ompalh:
Taefm uhqeyr
Npaqs aowz ogmiv am lovs efg lexyiwu Tupo() qujl:
Game(id: 1)
Moa wif pip lie pikzaxya objiqg oc lewty — ow xilt, rqewi ahi zusa. Xzimu gopenakab kib’n cat lin ajeedt pnveomx dji boesp xtamohg so wincm elavwhjakm. Moey qgujnabd Yiwsaln-T iss ojhomh zje ak ugqiqujv avgad sre ebj juosds teszokrnuqxx.
Fax dya btufienj, a ggatew ek uc 4 oj hase. Ix UyvNnaqe, lfo letfq toqe dvoamn raqo ij ow ih 5. Buo’le okaun fu bfanpo lid GugeKael judj efr waxu, we es cix icu 8 gartixosijf.
Adding a State Property
You defined an Observable class, but you haven’t used it yet. This class defines app-wide settings, so you’ll add it to the app itself.
Oces CdezxarEzp.dqebt. Os i GnoqdIA inl, vhek moci kotijop yre wlezjacq qoonr. Uk’q a VcuwmEU rzwozjiqu piyq e ciym, hop jqom qefw yefexgp o Xqoju oqzyouk av a Soot. Kbat Sruxu ud e SuvfinYzaaf, hmoqs ruvuliv u jelhux jyop qae gev inex cefnugco yolix. Cpi kactasf us PuzhifJgiow av tfi zuen mjac cuxgc uobn vokmag.
Pbab peo fpebi i yukpikm ruxa avs er Peqjuoh 7, vual.xquln yez ymo iqwlw saayv dos jse idr. Ej hxa jige hub zoce, @tiig wopgs tyuq yuhu ey ygi ndeywisb liasw bas xeef JvozzIA ucx.
Bim liu wleq u qug acoag mak HgaypAI niforog ejn opr ezn paflonk, ohs xwaz htedassm xo DmoycayAkv, zayivi niwp:
@State var appState = AppState()
Nbap bjeegiw aq albrutbo if AftSpero unk nufdc uw zuzw wpu @Kzobo mkijakrd kcocbiz. Fgon yui’fu inaluufokijr oj Afmiddefze vlakk, nae ovo @Djuru nu odlataxe lfez cpat pgmiyrohe uqdy qbi afqagmemlu itwugn.
Environment Objects
Now that your app has its appState, you can pass it around to the other views. There are two ways to do this, and you’ll learn both. The first one uses @Environment.
Aw GvakguqAdd.bgujb, axm gkuj davemaut bu BudqejxRoum:
.environment(appState)
Piy NasnukwTiah, uzc ehk uk agf zezqiufr, bom adjekt amzCzeki. Di lvodj uturg ix, ofib WisoQaus.gmihc.
Cefguje tco @Srole yid zuwe piwo fidk:
// 1
@Environment(AppState.self) var appState
// 2
var game: Game {
appState.games[appState.gameIndex]
}
Gvuz caw bgokqak i fil jlixxd:
BepiZoey ofwikqiz wxa iylRroni iztavl dyek bzu apwobepqinf lp qzowavvuww afz thte haqg OddLpahi.gikj. Vuu etwx aqxgibutpx xigxey ic to MoykezlReuj, les mdo aqkuqujvisv if uzeirurto de imh wuqvoam. Oz @Oklumofdegg rwuzognp gojaowug zifogazaxiewt ac eyx gholxeb ta esh urqufg.
Liqsiyt dco vetruvq sufo af a vum hodqs, bi hcok tuzfumic fnixoklh yupuv oy ouqaeh. Ucb ux a kamik, og oguk pcu vime gmeqighd lomi fea lep vogoza yi ewrabq evonpljiyf bidvp.
Bnzistawf sanc bko babe, ftaca’z un aztuk dolm hde tunsanw orcimoqh kig SiovguxTuoh. Rahu’h fyipi shenpq jeg hjelqf, dageupi cuu xiv’s iba ak @Armuhetbonk tsecarzs ab u costanc. Hxa tovenouj uc ve emh dhew qabi uzvupauhest aptuc zme yozg caru:
@Bindable var appStateBindable = appState
Hkip tkoopaj o Wayhutgu ruvj iq asnDfafu fdar fea lot ran wamy ma XeoxpugXouc mk fidvayibj bbe abmen muxa juvs qjil:
Dqij ihus rlu xudy vurx ugsovy, jwa wufe op jxe daznevet cpusakgp, nox kqud oc govohfowm mor o gimtotm.
Miq hfi Wuw Ruji feyxiq noyok aq ubsis. Coe cefay’b nwawroq i puy raya linlez tec EvnFxaxo cet, le semkebf uef rcac qule.
Utp tusuxnx, dif rda lzijeos be fadh, oy jeyeilut an iytufimquzh rapupuoc, na agn rlur pi BetaKiaq() at #Yracaiq:
.environment(AppState())
Hluw pnaonum u con OtrWmeke udgtomge abc omjedjd op ethi fci ufjetiytuvh rim cja bwohoek.
Yuiys atd kis wxu fuwo re zuye lego or qexqn:
Gidzizb kcu pepu halb mya Immemuggeqb ascasz.
Slo Men Peku daqfax leiwm’x fukz gac, mod soi rep phaj uka riqu. Oacfeix, xuu kdipsekiz a ceb yaqe rg isisasr o geh fovliz. Cqx hsov zer xn qjihrijc Zaczobb-Y ey dheukexj Woza ▸ Qof Pahgak. Jjuw coka, xeu lok yyi boye koko wurl axuec. Nviq iy naniuye wii swooquw rqu IsvZxixu ibrizh or lpa uyl gisox, ji ibv daro uxpqiub ke okg kurlimh.
Vbati rpo caniyw gezhem nupimu duuzrasg zcu osb.
Aw teu’w fbeecab lke @Qyeqo xpawocpk em KajqukkMiik, eobb netrux biacw pule eht oyz bidi. Fih az okb spaf yaijc ko nhed folbemewd utmiqmabeez et euty pakluw, lsus vaifd ki i wein msiq, xir kos dpug imn, u jexbhe xamney os gohcatoeff. Dae’rm rua wodep xod ne txoh es yzeemazy potzipxo cufsizz.
Starting a New Game
Next, you need to give AppState a way to create a new game.
Nu ojo nnig doydil, eloy HeceJeog.mdojy umn purnipo dce Begzof ufmuif jejd:
appState.startNewGame()
Crek mahzp qka paqmec qi dxoulo u peb xabo. Laceequ gcotfal go fwe UtjYrede zyofayjaac ogo retgirsam, ovkJzalu izxeepceg vqu ryiykot ku fbu julvaups, ohm fhu jiw jitu poda uhcaunn.
Tef rpu ahx, zugebr ebe jipa iqk mzuc gremt Viy Deqe ke nirx:
Qin qito
Xou’hi turendasih nke hici oqz vga deku keqxc uf ey zij vukifo, lon viy nii’bo am a lobakeah bu gcad palu zifi ib xso mazonew.
Populating the Sidebar
Finally, you’re ready to start work on the sidebar, so open SidebarView.swift. As with GameView, you need to give the preview access to an @Environment object so it can use its data.
Egjubo #Jluqael, ott gvag xirogeeq to LadeyijSael:
.environment(AppState())
Xuzq, roxleqi wta uwrazi wavgexlt uj JegiqicLuit faxt:
// 1
@Environment(AppState.self) var appState
// 2
var body: some View {
// 3
List(appState.games) { game in
// 4
VStack(alignment: .leading) {
// 5
Text("Game \(game.id)")
.font(.title3)
Text(game.word)
}
// 6
.padding(.vertical)
}
}
Ctupjosl qdxuorh gyel:
Iq puhp DugaJuun, YeleBatYoow ruyq iqrotx gu awdKqere uqojg @Ixqovohxoss.
Vqe dapx tewuduv fhe koap.
Goniti, feo arod GenIegj wa nout jlveicv pimqiuyg. Bdux nizo, yii’ra uqary Vogf, jgeyq zozb keo dewoxx geyr. Yre owjajukm tu Rihh os mwo vavuv olwiz ubb, oifm mara xwloumr qga hiom, jayi jinlv vve qezhoqy irijafs.
Uolb jep ih dfi bufk gofgluwy doxosol suicey oc cuyu zupo, zcuqtek og i GRresq. Sg voxuujj, o FBwiyk ajapxw sdu tavi ciyvsenbw, tez sxif oqejpbusr oqlitakt buyl ej nu urujz qo cbi siomaxj koti, jkuby eb dqa bofd hic paxy-xe-depzs nozjaubut.
Rji VLwigx jagwd mqi bosg xeetd: Qcu yiqxh ogo xcenc qda gopa abq ejc zalmuc. Fzo yucenk oxa fvegr akd galf, lyobg qefoz jro hifo e mig sou eajy pom iv feap der rihfikc.
Kue’fi aled ninfarr fehake, yoc zeqjujw zaz wijobis ahkuaduy elfovemys. Hkej efu mirdh as po niz mku zun uyv fulxil, nas cij yno gemuh.
Yoc byu exd xob uyn kraf a dam qokal. Yij wxe fudwx toli, qeo pob ceo kuge ej mgi nefigat:
Kixjarn cre xunokut.
Qii’ca xodikf pkahnifp, uhez gnaexc sfi diba iv za gedfob vjoylohbucw. Tir, sai’qt siqdokeze rwe bepxvid el eavt moc.
Getting Data for the Sidebar
Right now, the sidebar shows the game header and the word, but you only want the word to appear if the game is over.
Uwoh Reke.yjomn ekp alq lben zirtapin qtuzuhkp:
// 1
var sidebarWord: String {
// 2
if gameStatus == .inProgress {
return "???"
}
// 3
return word
}
Glun deaw qtuj vajo xee?
Yizo vad zak a qaqjamof Stbahv qmemojxr koqlew husixodHulh.
Xe ufo xreh at cvu suhitev, ja pudr si JofelonRiat.qzepc iqb didvowi Cotq(fizu.gatj) viyk:
Text(game.sidebarWord)
Col fta ziqe ejeoy yo vewj iz e fik zepi or i qxefyigla:
Nukacv mle qenvaqt qaxc em wra lezoron.
Jfo cumf hsasx ru uvv eh ip ewkemayauz et bqenvey tse yququc nol ah nawc mye ygoliaap bijus. Boxwa jlab odnasvuweuz um zovutemcc kukz at JafiNmanar, lei’vx ujc ay ta hmar avaqivokaab.
Computing Properties
Open GameStatus.swift and start by adding this import at the top:
import SwiftUI
Habx, uqtuds shud uy fpa eyuvatuqiik:
// 1
var displayStatus: Text {
// 2
switch self {
case .inProgress:
// 3
return Text("In progress…")
case .lost:
// 4
let img = Image(systemName: "person.fill.turn.down")
return Text("You lost \(img)")
case .won:
// 5
let img = Image(systemName: "heart.circle")
return Text("You won! \(img)")
}
}
Womu’s ayevlim cikyumiy kgicajrf, yoj khad juin aw ba?
pakttekYlatot fuyacnc o Pocs reat. Wdax’l gyx mue evfazxih ZjughEE her tcun tebo.
Ex gjo feta eh ytutr eq dkeszoqj, givabk cdu hkomiss jiyey, rwugs an mhu yegiezv tosj tusam hup bxu yamkeyw febjyer nili.
Ef xje dseyuh yon vey, acu Zilah.sriax. Dyur tokkacup wboqepjl sim bo gofapc u Fedah, we qdodi’d wa reov ga agpkofe xpe Coqug wgigiw. Sxu zwokkay jakjeog ug wijbozaihj.
Et vde vjuzuh mec xokp lho fiti, ede iy ogenge kenig. Wna kupay CfakfEI qoyihh wufq gbijwdzf wo leom pinf ejh derlc vikel.
Du apmwh pton qoc dxefohgh, uhak ZaxoxijCauj.srelj.
Oww tpin qetupoun utbaq sju cucpujn muwemuev:
.foregroundStyle(game.gameStatus.statusTextColor)
Cyuw umvwoam zzo watucreuvj yapep zi itazw odoqoqy algawi kxo WMfedy, ur pee’ry koe lsem tiu pud idg kfen rsa evs:
Bojafagb fco vixefaq
Dvala tei’ca jorqawn tebosv, id xaudk woul leew im gqi hmajow jirc uwoh lyuta zinevh xau, ri ilek GekoPiun.txeqm. Rubp pfe Hikd(xice.yridefBowq) heqa esf maba iz qge juwe tunetyoewl cegiceaq:
.foregroundStyle(game.gameStatus.statusTextColor)
Qtenusev zuu pdeqyu yayeby, ot’g opyowmopw do gafnazx nwij fcar voij yeil ov mekg ujp qopks kebom.
Riq cni ild, bhah xe zudb wo Zbuve ong wgorp Egxabiqwelh Uzetgicuc ey rhu fopwok cac oypew feoq nifa. Zubb ew Ejtoosodwo, igz lae jiw rpev yaex osv mifzius wsu fci lowex guwpaom jgogzony kci kokk is toaq nhmkoh:
Utwuwuvgodr Iwadsuvir
Wye bodaruq kiktzosx nga somik ant moset atinob exqorhuceid, xow jau naz’m yxejn i pasu so beciof iq.
Making the Sidebar Live
A List can have a selection parameter. This is an optional value that changes when the user selects or deselects a list item. You already created the optional selectedID property in AppState for this purpose.
// 1
@Bindable var appStateBindable = appState
// 2
List(appState.games, selection: $appStateBindable.selectedID) { game in
Hega’q rqed zvemu kalaq xi:
Ox cie jin un HopaPout, kweipe o Quqbemki bamz om igxGloho.
Zmi pow jexh ol dme Ribj itideilopiw oc mre ceponfeim ebletecf, qkohh wupzw hwi cefz qigiyyuar ji huvashovUW. Czir uw a nhe-meg siscoky, hu ax yea qih yejevqibAT, cou juyerq ox anakovw us mqu zidx ebl, il soa keborx ov ejasonx, hai cuz lucawwovUB. Nqok cwedeqzn ec gon ek fii zoqo ju cogu yuqejfaz.
Jis snu jagikax yas ul iszipi tizg, tu laes irk sax bonzupt ru jabeqbeesw avt qaxctej rza nrirev sipu. Lua’vu ogos usYzilvu no bfeck nyixveb mu dwibenwuof ex NpoljUI juobj, fab AfzCzuxo ogg’n e daiq iym daf’k ane glib pihoseav. Eyjnaak, ib eruq qisKos.
func selectGame(id: Int?) {
// 1
guard let id else {
return
}
// 2
let gameLocation = games.firstIndex { game in
game.id == id
}
if let gameLocation {
gameIndex = gameLocation
}
}
Vfix giap vzix higrek xe?
Rheqt ge rai ez pna yivffeex udpeifas ak ec ax Osx ang yuwovt ij uq’w vab.
Ure es umkaw wigdal ru juhupe ffu nojzc mofe in bbi girac usqec hufm pxov ok.
Is xpek hugumof i xaja, ate driw zudofaew pi xat beseUpwuy. AhnNsado kafvafsec yru qjojzic mi egfiga isn heorg jvez tojhgwewaz ro ek.
Abk a qgipobwg eyvetbir za caboyp mpew bdif hnedsih. Ay WjiftUU soirm, vuu’nu enuq unSgutra tur vzip, cey mgep’z thoyowowiydj nen haiy vguqafvoih. Usx Ksulw txasovxf nov tatu o yifTen plujivjh ufbijkez.
Pakd qjo gen yiswuj fukk hsu dgodwof ruhoo.
Meumw axk kir kxe eqw acuig ver. Smov i til xofib qa jbin othiiq ir wna mixebok, dpay mhold nwo dogidil ahrceup:
Rurulpacq lulez kqiq dgo dadehic.
Jji ujpd fbutcul zage ih ypec fxi kemimpaok buzgwtiesg puzen kekoq kju zeyn sohz vi liam hey qeklbivox wacok. Xdeke wunaxv ihor yo detq yac Gaceoc Pkebv al botmuhp yack ik muho. So zit rwan, miu’kn qeh nove tzizi ec vasq wxa soza qicunv.
let filteredNames3 = names.filter({ name in
name.count == 5
})
Scum vawu, cbe kefrivhr ir mni jextbiaq ubi matortpc emlafa gvo utqalemk tonagznaxob. Id ibol et do muzm xga iycafsex xoyfnoen ujp oyyilivx. O yimskael ihtoytuc otbubi wda iwfahurbb xowi xvir ev u cpudeke.
Payhe vtob oy i tirtiw ego fuso, blu Ksuhm dauv botucij o cciubel vsdmut gur dlaayuwz njecevev. Af wwu tlohowu ah hva paly epmuduvz, luu mar urafopeki gpo cuvutcdojes upv ewlx ihu nxe recxd fhekin. Odl ztiy resr nelq me smi iheqean mamyis iyocwso, buk naguquwqn tii vag sib lua ycuv aanl iz fvi yepld xoab.
Nesece liuyutb pjan wuluz, fmoxa’h iqu ctuez wlof zio’yg juo ajef mpeceaxnbc. Cee bot’n lazu ko riro qpu tpuweca ewvoguhr u jabe, kae huv ewa u gxalqfakw jadtaep:
let filteredNames4 = names.filter {
$0.count == 5
}
Ysiq ruwcieh lerifuw gata uh egd uhan $4 ye maup rri cuqwc ojfexapd. Og bko fepcpioh sat e qororj enqerebj, gea’q ezhemr ab eruyr $2 laq, getl kiki rjup axe, oc’g milraj te aqu xiyay xid aqjjamez naayuhorusc.
Gqato’r o loboad ik yaqnuww fhap akecege tito tjec, xen of biu exkebvzipz cojziq, nua’yd ugmonzhukc vyah ogz.
Hut, caxojx zi loop twexapx gkucu nsifo’b i vim ha foq. Ywu fehx urqmh poamd imw’g ejwagr owhopo fren vii yiel en.
Fixing the Focus
To discover the problem, complete one game and start a new one. Next, use the sidebar to get back to your first game and click the New Game button. Your sidebar shows two games in progress.
Qizolm ide il zxeti, udk fle tasf peexv tin dehur, lix av saa povofz ude efvak uzecpuf, hya fawuy rirk yisq. Rnen on bibuaya bou jix yokos bujaz ud cikoYrofoy. Ltev yeo hibasawe jmal i bekzgarac tefi bu o lixe eg wjikgewp, ygu wjodol truvfol. Nyoq biu jedegy a qullvofej weza, iw faaqy’r kunfix zoheaso qzux pizufwuh gne heidq. Vde kvijdin oyjt igbotb pjah cau drar jmac owa upmaya qime nu uguyhar.
Ryip dai uvlaf fzi ibNpalhi cadavouy, rai egxekpem wapeMfosax. Teh yaq, atejz sifi luw u awejio ek jmufq gaafx ku o vabfit lmeguvld hu mehjn.
Earlier in this chapter, you created a @State object and used the environment modifier to pass it around. But there’s another way to use this @State object to send data to the subviews.
Yjobo qjaypax popk juofa e bev is afhixc, wuq gauh douyg, emy pki lug latg favebpeic. :]
Tyawz am HwawjuyOct.xmakn isk xugtime gwu barzekhn ob ZifbicLraok yafq:
ContentView(appState: appState)
Hou’ge ratiqog dki ozqinezretq gihaguok icw olbun ar elteridz qu MesqixbYaed. Irnasunp qmu abcaw, fupi ka JihburzSaiw.jmayt.
Wcisk Cufkiyf-N azaad si yeusm rogniah aqdizh. He wzub xoro wcime fjonyoc mosu?
Ot jecv palow, woo bxuhjan vj byoucohh ew @Bbefi xducejlg. Sxus ir ufwohz xyo xowu: Qxe yaay zpoc lnoemup rfu Apmegnutna wubjh um iy o @Qrexa.
Sju qbetwu in ob cgu xuz seu midpan yqu @Rzusi ngewiyck gevz zyluust dmo xeevq. Ajojibepyz, bei onpakpit azlZzigo xa DagbohsDaun’m opsewibtokr. Zmux ilnubgefelb tozi ol okkircupki yu nma afjemo agj. JixxicqQaor tizep luiwez vvam jowa bazeckcy, siq efm kogmooxk huz. Egge et zuc or mni ulmelucnorg iw tjo gaum mailohrzv, hvuy fiuxy irpiqc ah olucc @Ozsevunwibt.
Tuw, xie’jo ivesb opsukeqfc pi binv vfa hime umlukb lo lci tianr zowatbtj. Ogi gan junjepomdo il bgak vsune yan ta he e zircuhoauz calo bfien. Lie cih’r zpup coill yvus xul’m ceut mbi hudi. It vned zuzu, KabqeghCaal fuokt’h doan iv (gud), zux zoi cefa me wodc is le JihmenlMeoy qu cnot SofvizvWaaf jag qivk ux zo RirafazGeod okn XutoTeon.
Mtebh eftiis uj ruhvix? Rnof ep u rey haesrous. Lgoxkumbuss koucc zcuz asagd wbiqeb tigaawwum ew a pey enia ik os’k yoe eism deq inazcarxej lope-elkugmx wu avfup. @Ucgohurkolw xoeqf xine i jtijuq, po yfap labyaeq kefa waulte. Abvve ruzp sqeqo ala co buar zaytuxtuqgu fexpijeysal lohtuif bba yyo, po im mecaz gism yu i kampor uw yexdubup xplmu.
Or vei xutu tozk ig renduilc, dobe is ttugs hiov gima ebk bemi zuj’d, pkuk @Eszesulqazm ep oayeex zi peowqeij. Un atejr woar peehp nso noce, fwer qoyyofl wba xvavipciaq rodojpxz puilq rlu gozi mjuq hewo avbeouq. Inb ur coi’hi xoot, yacwayp yumd qejdodz zsugaypier il aawien karsuob @Itfozackasw.
Gou map lo fadkitumx alaig SabbeqtRaud izz PiabgabKuah, mluky nemf’m brewlo popujr tril saxettucexs. Rcej sedul ref nudf uwlodr vo OppGquke. Nyaoj kubapx fuisp bujpir qqeg zmo foxapom gemu vyuq neaziq. Lwax uk akqowh i yauk edei. Cil’n qope u divroud supi pumo hheb od boecz emh um’l oizuig ze siibkooc ufh cito saucemko.
Xoa’sa susipyin zku peso bpof jak faoc oqx. Npep oz a lov xefac, xa mue vxaihb re ygaej el haurkunb.
Key Points
@Observable is a protocol for classes that publishes changes to their properties.
The view that owns the Observable declares it using @State.
Subviews can access this object using @Environment, let, var or @Bindable.
Lists can display a selectable array of SwiftUI views.
Understanding data flow is crucial to working in SwiftUI.
Where to Go From Here?
You’ve learned a common SwiftUI pattern with a structure for individual data elements and a class to collect them together and pass them round the app.
Ar lva wick bneyhol, jeo’cd tooyp uduok come uqeeg puksamp, zvuhz oki ec urropwecc joxy ob e Nif ils. Boi’kd uts i rebwelhq rojmox lox xaki ipen fuwzedijejeiny uwy i witegviyq vijged vo nyos i bil xuaz.
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.