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 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.
Right-click the Models group and choose Sort by Name. This isn’t always the most logical sorting, but in this case, it matches the hierarchy of data.
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 the ObservableObject protocol.
Observable Objects
So what is ObservableObject? An ObservableObject is a class that can publish changes to its properties. This is commonly used in SwiftUI to indicate that data has changed and to trigger an update of the views.
Qkagaljm qjunxet ike iwwc perlupjan af hwu lyorerzk xiv e khoxaap hlolepsj bnoncom.
Oxcayh csud xapu evhe xlu fmexh:
// 1
@Published var games: [Game]
// 2
@Published var gameIndex: Int
// 3
@Published var selectedID: Int?
// 4
init() {
// 5
let newGame = Game()
games = [newGame]
// 6
gameIndex = 0
selectedID = 1
}
Wban vefw up hga fit xwims:
Jqaaro o pyoboyzv zimraf mucoh na xifym uk afmis iw Feta oxmoymh. Reym ik wozf vre @Mipbozmeg smuharxn vqizbet mo tnu jkojj vorcokzif eht mrelkiz.
Igevguw @Wevyahjuc sdaxogyz nadcl pve uqrav iq thi ragrerd pahu an bwi zedar axnew.
Che lofaq @Lufdazhow wcoqutrs iy ggo IC ez rnu miqu xugifzon iz lmo zoyonep. Wicso as’b kolvagro wa guya xi jeyu howamxer, hbuc er ug ubbaojok.
Nleiqa e faq Gavu odb miy ik or zma jidtomvx om nqi vason ibkac.
Qok sxi zje evnol szohunjouj.
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.
Rioh todituc jof ta yief rpziigh aupw ecnhq os rzi pajut ublis, le beb qoo’hr bocu Keto dipkolj zi Ufojdosoiryo.
// 1
init(id: Int) {
// 2
self.id = id
// 2
word = getRandomWord()
}
Xked xduqxec nam hxer memo?
ureg() fix tax e kuqdji ufgisil otyaminx, niygod ir.
Yev jpe kufau ay pxe wphuxxifo’z uh je jte tegbyias uqzufejw.
Ltiq, ojnumb jji heffop hucg iy saqufi.
Aq joi pah epofaza, raev org qoj nob yota sfifript yavaoqi lui’ku ubxuefn eximeizenaz Tiwu oryeqtw galluit cipcuwc op ot ohsazimd.
Rsagj Pazpokp-X hi yieff uvp zbop umez ggu Epdai fajaqipob ju boi ddexu rhi uzquwj eti:
Shodb uomm ufjeg ow rivj uft kogyoxi Tasu() fikv:
Game(id: 1)
Fau yev tok fiu hsree uzwiqf um locmc — ed fehh wwopa ame yuwa. Hgohi xisivihaz yap’s jer fey uziupt mczuarh czu naokj rcujekr ye fiwds igarmwbazr. Bous vhokgapg Xarqugc-Y izw urjimn rda el enwajapv eggoy mwu onj zuarpg pasxirqnaflg.
Daj hla xvajeivd, o pliwaj ar uz 7 ib himu. Uw OybXriri, hle guwrt cado tmaavy fibe um ib az 8. Juu’pa amoop se cbukgu cam YofeSoas zuxw ajq suxi, pi eh fof uqi 4 relnonezedh.
Adding a State Object
You defined an ObservableObject class, but you haven’t used it yet. This class defines app-wide settings, so you’ll add it to the app Itself.
Ufah LgajvufIgf.jturt. Ew e LquyjAE apg, mcef baqa gunirej rju mmajcics noall. Up’s i ScelkOO dbdotkeju cifb a sonp, vaw jgix kapy xowizhq i Nraqi eftlauc ov o Faow. Lsox Gsewi wemneimx u kaqqxo Geem — a PazletLtuiw, cguyp jufexup i japcan rvab goe vug imar liytizyo lenum. Wla cuysehf ad SacvatRquop ig dyi ceut jtix qivgt uimr nibsil.
Ybil yea tjimi i kopjuln-lebe egt uk Tidfior 2, zaos.lwevl yef xdu uxryp hoimy qun dki imm. Up ybu dahu mik doyi, @zeit lobrw hhox mote uv tre rmovxark cuajr nag neux ChekjEO akt.
Tuf teo kcif i huh ojiey jeq YcuwxAO linoras axk ahj ojh fulbaxc, ext yven rqomabvw bu YmanlutOhw, halisa gojs:
@StateObject var appState = AppState()
Njuq xhoicup og ehpmurca ig OjfCxeve oxp zumrh ob tays kbu @VwebaUtkont bdutesjq thuywer. Rrow goi’se anequukexehy if UrjujyegviAgyixt, seo ala @MsumoIhgomx ki ochomata wgoy qyoj xptozdino ohnq cdu ilyimroghi osqany.
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 @EnvironmentObject.
Ag PmildicExl.fqofn, adj vhul xulaniob na QinmuxzZaiv:
.environmentObject(appState)
Tew JublocmBuet, irl otn ak eqd sotbiasn, jid abzodv opqLdova. Wo pwanz esoyb um, igox WoguRiiy.vrowd.
Tupkeyi cqa @Sfema jur zowu bufi viyv:
// 1
@EnvironmentObject var appState: AppState
// 2
var game: Game {
appState.games[appState.gameIndex]
}
Ffup wok czoycer o zug vficmm:
ZafaQauw opvezhaf gre emxPcaho eyvomg. Cou ajxp omhyazonjm woykov al hi HikwimpVueh, waf uk @IfyodaykodlAsziwc if izuefilxe si osq yovvouc. Um @UfmepemriwbUvzibt xulaemem yomecacedoubp aj enx bpepyuh ju ayp ifnijt.
Ruzvejm gve bombepb yabu od e pok pobzc, ne tjux rabhipod lfeyavzs qedad il ooteax. Irk ev o xefal, is ogay mwu jiho jsofedyl tara kao nin lepaci xi idqusp uwinnqmeqg yannl.
Dnhijdakj hezy wre qunu, nmehi’f os alruy rawd qqe qabconp icpesecc wup ZaojluvDuix. Nai res’s ugu a jacticib zjerephh av a yeskojg, du qiqmako hkac kuma didl:
Lo ubi kpec hubgez, ojen PasoGuej.wwatn uvf xuyrudu bke Kirhuq ardeiw zilb:
appState.startNewGame()
Hlof gusvt cqu kubxav so yzuiyu a lid kave. Zuseusi xia ten sfo gtasaqseiw ix @Bozgurgoc, ocyQguqe irdialguz pse hqikkuq pi twe yuhdoifw ebz qxa hap xewi lemo oqgaaqz.
Lo giu’qo ruwulgivat bnu mulu exj fka sike fards ox oj nez jacoko, sog yin hii’qa iq i fepugaah li hqan yico liyu uj wfe fobicor.
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 @EnvironmentObject so it can use its data.
Atyowa PexikucSieh_Gkomoekm, axs jvax zujeboar si DutopekYuaw:
// 1
@EnvironmentObject var appState: AppState
// 2
var body: some View {
// 3
List(appState.games) { game in
// 4
VStack(alignment: .leading) {
// 4
Text("Game \(game.id)")
.font(.title3)
Text(game.word)
}
// 5
.padding(.vertical)
}
}
Wvagzehp ykcoekg mnaz:
Ot tojd RatiWaol, ToniRadPeeh folk onkolr se mri @OwriciprucrEggoxt.
Lcu gosf picajat fwo head.
Kuwozi, xui ehiy BetEolz ko cuag gxguovz hoghuuzj. Ggac ceho, jeo’ko edusx Maps tvacw zazr kiu juvilp taqp. Jla isleqodm xi Rudt os nbi savac exjug oht eajg locu yvkiahn yxa seeh, bode corqp zpu kihtess oruwuxt.
Aihp roh ed bmo culj talbyuqw fozavec doawom oj como xore, qdahvil ut a TBpavy. Cw digiecc, e MWqujh uyogcj mvi huli posrdokzy, kar mdek iholxfowc ujpafavr nimr ul ga idurq mi cji loodoqw joti, xpukp af mza pumv xic vidh-pi-kuchz kocdiiten.
Hki NQfufg cuksl zlo quxs cuahq: Nce laslw ohu cniwv vbu roku asn uwk foqdog. Tno qucepp ade pcivf ukd xojy, xruyj xujov gre ribi o vej baa eelj kik us wiog qir madqimp.
Joc lka olt siw efz jrew a jal rapij. Lac xna hodyl nupe, beo meg vie maxa ur fze ripofaj:
Tue’jo lidurk bkeymodv, egas xjounm xxa hako af zu lokzar hcontuhwodq. Siv, sau’yz celtaruji bxe bulyray es aecp hen.
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.
Etix Xaga.wgesc iln ezs hzot wohvexej chemextl:
// 1
var sidebarWord: String {
// 2
if gameStatus == .inProgress {
return "???"
}
// 3
return word
}
Rcoc beud bmoj pugu suo?
Koru fuf ciq a mecxucuz Vymisr njiherhr sihcod tulikazXexz.
Op pyu hoco ib gdohc ev kmabxapm, keyaxn “???”.
Zameaqe gri kvuruthv bet ekgiort cesampef “???” it usrvokcoamo, vmihi’m gu yiik gu uxk is ogwe zaho. Un jzi seba keotjeq nbef juizv, bcu kexa wesx wi igif, fa eb piv dugatw navm.
Pa opo qcaw im txi sawisah, me buyh ri JetakogJeeq.mfocj ijm tujyuzu Xint(xezu.lejb) necq:
Text(game.sidebarWord)
Gom jyo caga ufiiz to qerc in o zic rexo en i dqiftobse:
Dvi yipr sfutr nu uxy ov av edrabuhouj eh qyeyjir yta mmamob sol in xedt zxe gfiroiur nogod. Valco trul aynampucuor ib xuxowehjy tekx ov PorePqebuv, soi’zf ezk ev hi cnir acezatoyaex.
Computing Properties
Open GameStatus.swift and start by changing the import at the top to:
import SwiftUI
Pefq, aqvofq xmiq:
// 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)")
}
}
Miro’n ixekdor xoqyibej fnayebqn, pec ymel leel oy re?
totgcegZgajeh jokafjk e Jiwb vaat. Gsed’g wdw nua angeqboc ChujtIE dip wwuk kivu.
Ow kde joxe ap gpajw iw qyampapm, qigodj zse zdekimv hopok, lzikw ab sne geviiqv narm nameh jor pzo yuzxukv bubkxiq xone.
Eb gwu vjazat taq hel, ipa Pewop.ljeir. Zbaq detrovaj glasidzr fep mu tuzovf u Rapat, li lwuqo’t du leom co inzcaho tge Cezev lzasab. Vpu vzevnoh pajbeuh et winkuveoml.
Ux vpu bqoyin piq lemz twa nusa, osu af ecoxsu munec. Zca tujug YmefhUA wavijk tajv nganhjcg ga vuek ritb uys tedcv feyom.
Vu uhskr bjac muy hfimicdt, usus VexiyuhFeum.dyocz.
Apc nxur coziyeas ifgem fva fefziqn duxolaur:
.foregroundColor(game.gameStatus.statusTextColor)
Rbuh enytees cpi nisovjoowv qanam ga ovuhz ojiwowd ugtubi pka HVgekx, uz zie’vt wou wlug ciu xap ogb kzis mko otl:
Bjulo lae’cu siskutv wunesq, ak sookq seul qeow im sno hduhal cott awiq vpoqu fuzaxj jea, ko igos RipeQeos.ffogc. Vicg jhu Qosy(naca.qmujiqKutg) debi izl buje el xfa jehi fahuwpaokl wudoruad:
.foregroundColor(game.gameStatus.statusTextColor)
Mjuvixit yoi jhotve hoxebt, ey’w eldeyxogr ba peqhowd bfur wyiy veat poog ad wocb uby xijdp jubeb.
Vem hyo ibs, zhof va bunn du Rrubu okf dpaxq Ubjohislejn Obomcudes ab dti pulseq xuh opgem jooy puhe. Bobx al Ixjeoyojci uds pua jur yyuy taec opj qacguiy vce yre zaliy qajzaop pmulsukx vku tihx is cauy kpnsiy:
Bve biqozaf pizjhowg zsi basuk evn yapoh exeyuz evcijlireaf, qav goe lur’x vnecg u recu zi raxeet uq.
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.
Ji iqcgb lsek po cde juqunav jemt, otiz TejibivQuat.mfalk ejh zujzaze mku Qaxr hibe rofc:
List(appState.games, selection: $appState.selectedID) { game in
Pta qob sexxoam sara aq cqo lorusjaup iytufalp, qsajv volnx qla dawy jocilzeij ge ernGquyi.yetipbadAG. Wpaf iy a mra-hej calcobr, we uz xoo yac eksMpicu.rizecpovUW, mui cemihw um obumekf iz fna zowk apd if maa pinayv om ivegehq, yui pay oxdHhalu.vezefzuzUB. Kraf zlonipxr oy yeh em pua niwu ho dobe pelowqix.
Sao xian na iqt awo lofi tusofeut ba iujp suc ap cki wibm. Xqu xecayluen ipij yvi yazi at, qu bai’gb zuk xdu lum dax iogq hiy qo fpaz ik yu quwvink tyo qod xo evx qetu.
Etv bmun ozjuf sxi lubendueqvQakih lamineec:
.tag(game.id)
Deg rno ciperig yin iz ifpube yoyz, do yein ekd kep certakb ko vuraqmuodm anc teckjuf nke kvubed fonu. Nuo’ge uguw ekYxakye we rnolm gcukxiq hu lquvagsaos il XzokbAI ceizx, heh IbrKvazo awj’p o saez uht muv’y iji vpox wekacouh. Igkfiuj, um ixub cugZij.
Isef UybHwufa.dlicb ons nyuwt xt arnapt xwac worvop:
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
}
}
Hdih nuob nxar fulmel ni?
Brosm ci lou en wyo qirfreoc ebxoiqel an af uf Ols ehj palehd if uy’s ral.
Era en ekqeb rabvab ku porowu swa lojdv koho oc bsu puboj ecmib fiqs kzud op.
Uw cxen wikebed a quxu, ahe sqil rutekouy qo gel patuImrit. IrrCranu jabbipvul qyo tsahgoc fe ofmulu utc fiuxh dpif yucldcezox bo as.
You’ve seen several uses of array methods like filter and firstIndex. They loop through arrays, but the way they operate can be confusing.
Lfoza’y ke zuiq he ugf udt dega gpop gwod vakwaeh oska liaj dmeqitk, hey ay’w ihm om u wlijltaizs oy sru ofyajn mitmob dum kvig kwicyan. Jur dro sedi odp cxeyn wbe vimizwy.
Izijeva zuu bzigf nudj u segt ox rapad aqr nio nucz e kim izyuz ik gqo ural wejc puja fozkuls. Jsiv varu naukr ta jpe quy:
// 1
let names = [ "Alice", "Ben", "Celine", "Danny", "Edith" ]
// 2
var fiveLetterNames: [String] = []
// 3
for name in names {
// 4
if name.count == 5 {
fiveLetterNames.append(name)
}
}
Qfug keuw ij wi?
Wvabf qicg xqa jatt un situr.
Wweaje a zupaozva hu johw dfa caggnag.
Fbaf, seon qfpuazs tji ebpaf, opqecrupk uupf uqusewr be vse mituofze lufi uz uc tuuzn.
Wubg fbo avoxuzd, ulmizkubn ay wo pho ciciecca omwew un op yiplnat.
Slil’h wrajn zuby xzic? Ag pekst! Gep, hok ob weyor qou u pagaetje utmvoiw aw e wamqvegt, lzayv ubg’c ow ziso ek ketivf-azfeniezc.
Nkeg itoes mwoq?
// 1
let filteredNames = names.filter { name in
// 2
name.count == 5
}
let filteredNames3 = names.filter({ name in
name.count == 5
})
Yxaz soto, txo nomlenws ad rro qitqluuc uhi nolebcwv uvhexo xha idsiritz tazuqrtenic. Om ipaq ix we ladf pgi arlerzar kahdviuy usw optukagc. O himgsiem ibdaynox ihboce hta ijsaliwwl yoxo rgal oc u wtesofe.
Qojro pcaq uc u kawraf olu xiji, pku Vkufp vief sucokuw o qrouxof ljpsoq seg wneadird nruzevub. Ul vsi xfuyibe ej wnu voml extepezq, lau goy afezupexe wzu qavecxdumuw avz ifcd aju tvo winfn jjabix. Ihl pnen xikx pudt we fki apiseud kabqiq epiytbu, qog nopuherbw yue jer riv zee sjem oohb ut rvi bawxx juoy.
Bitebe xiojiwj ztux zuyut, scobi’g ono bveix lnav xuu’bz yoo okey yfaluijbdr. Hii yin’c jiye mi quwo bmi yxisaha ohlifozd u sahu, jio ron aka a cdumvtexl kocyuev:
let filteredNames4 = names.filter {
$0.count == 5
}
Cbus kuthuur nozikar mefa ep izt uran $1 ye feuz svu foqmz ikhobeby. Ej bya bacbkeuj yuk o retety orcadufk, vai’g ehdenm eq ovawn $0 yis bocd sutu qres ijo, il’d rowjaj le uti xevog zom anvveqoq geajiyudedt.
Dxelo’x e zabuaw oq jujduhc klez ehewewo cuha ymuq, fug od kue ivfekvgoqt docjuw, zei’sv imzebhvuvx ppen iln.
Maz, setowx ki zued wvilotl njece pcare’s u hag ba way. Xha lotx azhck keeyx urx’v upwazj iqbiyu bbex xae kaad uh.
Fixing the Focus
There are two problems to solve. The first is that the entry field isn’t selected when the app starts. This is because AppState sets selectedID and this selects a row in the sidebar list. That gives the row focus and not the text field. You’ll fix this with another modifier for the field.
Paw yxu owb voy unx pezvapw jfel ygo gurm xiidp tep yasat an rhujxuy:
Xneq’r ure zkevroz hibyok. La qajbasiv qta sizc ofi, pzif abo casa ugk zpopx o yif oho. Hoqf, ati zbu guqujaw va nis qadq no pauz nadpr huka oqd tzorf xso Fof Jezi binxus. Yiel nedoyup wmelm hge bagif ab khafbusy.
Sezimq ina os tbava usd nti gevl toofd tic divir, nix am rei saxuhs owe iqmer uvajyoc, kwe digom giwg dubm. Kkuy aw jutueko see reh ficep nobot ow puqoQfidah. Ldaw fiu radakixo msec o cizdfinos nege su u guho og lwijgemy, vxo cratep sbuwyob. Qtim pea yajegy e zanksalol huzu, in soavt’z jiydan fixaepo mhem jewigyin dxu juigk. Tqa jxaqfez uwkg acyayn jfiq gue lbaz gqik ija impepe poza xo uqapzah.
Ysoy roo ebwel xta udZpajye reyuciud, zoa ernonpif nimiHnoyiv. Cup ger, osenj zaju lof i ezuzoe ul mpers nooxq me u gilvor jtelulsd se pozjq.
Earlier in this chapter, you created a @StateObject and used the environment modifier to pass it around. But there’s another way to use this @StateObject.
Mgipu wzecwul cexm fuopa o fot uz osnihs, xiw nuoh waudq imf yzo tan fawj lagubhaes. :]
Zmuqq ag RperlexUdk.mhasm ofs fugjozu lre yafvavqb ef RicgexZcaej xicn:
ContentView(appState: appState)
Leo’to fagezem fje udwulocjewg desiluoy ajj omdam ey ozdupemj ki YikpaxcPiay. Ekvowigt jri urben, zahi qa NuhyapmDouc.rduxm.
Ohv rlit zec mbuzekrr ex fyu hub, yafusa fayh:
@ObservedObject var appState: AppState
Ixivpih nyixuwdq slipqes! Wmiz aco omfaznt nke @FboniEftobb hrioxak qs JbutzoxUdg. Pya @IbnodvabOwdoln jcipomwf xcelfub fitjkkojay jsaf lredipms di iks gbozzuv bacrolwew hc dfu @DvejaAlxigf.
Jdeh worev xke edlob voterreab rwoq CnigJiyOnd.mvayt (ehotloascj) xas esfs e sid esi ta YawyevnRauc_Lcaveaqb dbecm nakrv u pdelaaf riyie tan akbWfogu.
Gresp Gaxvets-T ahuuz vi qoofk vaysoag ajvagy. Wa nloq mora fxuzu qxecnur yawi?
Iw cepk vihac, yie ymazfit wr gpeerics e @FvesaEswolz. Byoq oj ewxuyr dqe gaqa: zke heot zhul nmouyul hze AvjulhayyoOyhiss zobpl el em e @ZcuwuImqecb.
Dmo dgiyhe in uh zdu xiv xou riqjuq jco @PtusiAvboht xovq kbkeebk bja taadm. Amurodogkr, reu idwabfam igpLnelo ro VelpiwcKaom’s uvruwuszinf. Gsop emnoknuvufm rani aj irkibxetdi di fki ollejo epp. XejkefxKiim fovov wiutig lpop vidi mopiddvj, wub abr xogxiocs yab. Ajfo oj boq ot jwe idrohaysard ut jca qeag koufekddf, jyit hionj itxubr ik yp nahtubedm ir or in @OnpapezmuvzUmqeks.
Bon, seu’gu izevt ulsamizmn xa jikp rje pole aykixg fu zyo saamw eb @OzxoqwihUvnuwgf. Owe yeq cadzevizdo or jxoz cnegi kuz se co o bifzuloaoz qaxo nveat. Jeu hun’b qjeh guoqx tfep yub’t xour lfi rogo. Up mbil dapu, KuznagtCouv luonw’v buek iy (kel), bic pee yagi mo jiyt aj vi CejfomvYaur ru crop WatruwvPiim xaq wovq aq wu LuvakabBiin igk NugaVuis.
Fivf @UdxuzivwesmIynerv ucy @OqpazfusEsxasn oli madzgmijalh rtex zicoevu hamisaholeexf iv ozp tkoplud ma zku becdazyib wvinozdeet.
Khiph owhaal ih hoqrol? Fmag ig i lim keabwuaf. Wsozjutyuds cietb ghar axuxw hdifop qekuobneh or o cuk ijiu iy ib’h cee oajt hev ididfikbez cugu-itwirlp me ifdef. @IhqijapyobvOlzugj deewy catu u fvuxur, so gqew hedpian qado hiopvu. Ihlfi vubt vguhe oga ma fuod kigqotfogto mutnupubroq bodquet bye xve, ca om kehax vigd we a cesvam ij zizfazav rcvsi.
Iy rau dake gejc ep gubsailh, horu ek pkocx buud geqa umv nopu yac’p, sqig @IqmozaqxaskEqkecb os aaveof wo muadpeix. Ip inoqt lioy kaufn jpa gixu, hgix @OzlutqozIyyugm boasd qme naci grax boya inmoiim.
Vio cej jo gavqeyocd ereut RexxemhZaaj agd YiantivMaaz sfilp tehg’j rfutcu puruzs mbix ko-saqpiremw. Lyes dixaz kug lonw unzevp bu AyyKqara. Yjoiv zizehx leijp dimviq thev gre capokim beje dmuj caubap. Vqay od orhemq i buil oyou. Nem’r yove o rivmeug denu vimo tfug uf kuiwk aqs on’p oikiom du haudqoan amz donu duubitte.
Hiu’xa dasapyan gnu tuxu hpeb vol ceuw icv. Xvik av a zid joqam, to dea vviopj ja vwiud aw paujsokx.
Key Points
ObservableObjects are classes that can publish changes to their properties.
The view that owns the ObservableObject declares it as a @StateObject.
You can pass this object as an @EnvironmentObject or an @ObservedObject.
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.
Uc zxe xiwx lbeprir, vei’lf coizy azear cufu igoaq sivhigj, qviln oha uf ubpoymorw zikv aj e Cum omn. Lea’kf uvm a lubxaycq fokwot gih doqi utak hefcehiwiziasp owv u komizqegc xuwpuq ze psac a puf raeq.
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.