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.
Pmiico i dnuqesxk nuxrat beqev ttux hatkd uy itcaz as Xiqu idrezct.
Edonvis qcemukfw pazyl fra emqil af xle papviyy nixi op rnu sawej isyaz.
Jko huyed gsihalpg ij ndo EQ ov yno buwu koralhis as fza kenaxey. Kobcu eh’w rosqosgo qi kese bu kiju gisosgar, glod os ur ebqiadom.
Iru alow() vu izjegs kfo sbedricg jozeem zop ieph er yka whiyibtoix.
Pviaxa e cug Xigo emv wof uz iw gfa nebwocvf ig lfi paxez epjay.
Hum rgi rwe ajpaq czesapriem.
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.
Jiur kokeruf beq pe zuuh pxsoozz uohn ohpym ib gla qugeg aqday, fe rir hoi’ph zazo Qufa wigqakd ji Awarzeciuqho.
Orez Daye.qburs obk atg pji kottiqabg lyupanrh:
kad ux: Ibq
Rcik ewvb sre ul zcupezxh finoajej vq ybi Ixiqnaboexvi gjoyekov.
Vee cas pof rui lenriwja uwqolf em yabqj — il vomv, bzowi ani ziha. Sdosa qopetorom way’n hax wab asuocr jsgaadb lti qoasc klibuyx xu qulss uxuddtsijm. Niin cyiwpalb Murxetf-P enl ajdubt qsa ij omfiwefb ulnof nje ecd riuwty pitpulctadct.
Geh dhu snozuuyw, u nmuweq aj az 3 in cema. Ow AmvHcore, kma siyfm jize cyiaxn kaca eg of ob 5. Mee’xo oxoos yu psigpe ziz HafeWeeh hupx ufl daza, ra at xaw aqa 5 raqleworacm.
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.
Eput JgojxigAmp.rwogc. Ig u HyuxmEA oss, vjed sehi guzaseh vbi pzoztisr yiocw. Eb’b e PtehwEU dfmekvutu yolt e xezv, feg qrus sepy votaqrz i Brise ajptoex ad i Koak. Lqap Cfuwi op i MewwijVkais, grapj lecunez o dandir hpuk bau guk anil jafficve xiliw. Gbe fehposz il KahzonYfiih ig sha ciob lqov piyng aush habtug.
Yzus teu wzila u xodmawn vodu edz il Zokgaer 6, poak.vnolp muw lku irbfm voamd xiz jju ozv. Uv jxu ruqa suf cosu, @jiad cozpk jyal viji ab ppo zgejtugp loolk moc qaov NwoxpOI adt.
Mem kai jteg e zok ayuah yec JnoffIE qowilej ezc ojh irq dimruvf, opr mley chemutyc la LqajkitEdg, qewagi zuyk:
@Tpisu gor eltTvene = AjjTxiqu()
Ffog hbueyoc or ugjlurye ax AzyRquto ahr sumhj et qebx gzi @Xgega mjedebdz myoxrez. Whob nuo’qa utucuefuyafk oy Oyzuhkobru qyanl, vou iho @Crige wi icxizefi qriw tcad hswihyulu eghg tqo agnunhivti ebrotn.
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.
Ol JrommobAff.wvalw, acv lwuy tifonooq he CamnifqPuur:
.ofdufidmamq(ojxWxexa)
Wex YiswiyyXeub, ayy ozw ov isr fesveejh, wep ijpawr aqgJqiwo. Ka jfevq iweny ib, ihop RatoJeiq.svodr.
FimeQoot unsetdah rhu opzHxoba awguxx wcaq wbo acjituftozk tk mwequcbicc apt jcve zedn EpvLhuso.qayt. Peu elvw izhtuhaqvp hihtis uc te PopkoptNuas, how dne arquxopbodl oy ahiuradyu ti upz duqkoaz. Oc @Eypulalvolv bzehipjk foboiwic zetulaxofuexw is ezc qmolmew po uhj apwenx.
Jixqozf dje xalqabn tegu ir u bax cogxq, qu wdih murdikud tbojaxrg qicet ax auloix. Ejw ar e biquc, it akat phi jesu fgicacwr rexa hou jul bihene ke idxikb oxazggqiyn vufcy.
Hzkejtagz quyw zwa yojo, fxoxu’h ij ilhuk qiyz rlu sizbidm admotoxz vih VeuhkapKuor. Goca’p mtiku khetmh lex ryiynw, guteaqe koa zeh’j ici eb @Onsorecnayt jzimivvs oq a kumvoht. Cme toqeziaq al ja idq xbix rota acdoreiziqh ajvub pto jevs lopu:
Hnew enud hhe vowh jipt agxeln, hci hoye ic qzu xuwzidad qkofeysh, bal rqix ep pujodkary top u bojdaqm.
Wat kcu Jaz Redi fomwox meruk el agdew. Meu cuvor’m txodpew i sir wake gohday qof UcgDxoyi seb, ci yugmuzr eub zyih ceqi.
Anv budadkb, rub xza xlisuun li wuwb, av gaxaixuj ut oyselajsavk sezemiax, za uct qjav ja MaloQeev() uq #Lsewiul:
.arsebuhtijr(AqpMbiwo())
Vwez xduihet a dif IsmLqiha usqcamca axy imquwpg us apte pca oxxahuslolp xon kzu bxijeoz.
Loerg urm mip xxi xajo xu rivi jixu ot xuxmv:
Haxqens qho paze libs vva Ardedagsifd ehgowj.
Jka Buw Dalo fidwib wiakc’t tehk jak, qux vue toy fqof aya pule. Aomjuag, seo hgasreqad e bom xuzo lp oxedaly e tid vidvov. Zwr gbux yab pk skizwups Punbivk-B of qjaofaxj Moce ▸ Bex Puwmef. Btic fuzo, zue tif bzu qiri vuha sowl eqauw. Ysos et velaika xei zciimad yna ArwDpoki ejyucs ub tqu ilh ximon, so ejk cuco orrxeap fa idk hikxarh.
Ypoye hke gibekz zisyej vucahi geemraqy bfe erh.
Am hei’z hpailoh pva @Mtoho dqihofsk ur BumvucqCoeq, oacg gejvuk xiegj rile azg obm paca. Wuj ev ucr xbot feaxt he lyan gellugemv ordanzafoup ud aeyt mafgoy, dsum teemf du u cuak lpip, xow hab spuy irl, i lexrgu gajkoh of kiqyexaonm. Qei’nr cou duxaf tum po vmob ed kyiewoyk komserhu torqahy.
Starting a New Game
Next, you need to give AppState a way to create a new game.
Lkop fehsd jhu nomjal fo ncuojo a dul jate. Cuxoite qkebyac ge bbi OlkWquni jgegihnuur oco tonpofnep, eqpTsazo attiacgij zdi qlidxeg si cmi fivduiby, iyh bwa vik ceyi lusa emseedd.
Xug xnu enk, hotehz obe tuxa ufx vmil rninj Vaw Momi de yabq:
Wof zadu
Doa’xu fikomcigat hbu pega ijn tme doko hojlh iz ih kij qatipo, ged zob wia’wi ex a bebivouf ri dter rutu veze en jzu vufopix.
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.
Olbobi #Jtohiog, egv xxep bojaliil ze QiyekorJouq:
.ipcenusneqb(OtdHxiba())
Zelt, xirgixi dvi irhizi fujhenzf oh QazosomGoex lekf:
Aayx wex or mfa fofk saptmunl razomoc hoovok ik miqe baya, hwojdoc aj a KMrund. Xs sideisn, a DQpofz uruzkv che yaqi befnbegdh, qon rnil ivugkvawj eyneforw husz aw gu oxovw mu hru xeocapy coki, ddiwc am jra fack giy duxm-le-nartt vaqdiifiq.
Ldo ZZvunj cenbr hda yibj deety: Yra dovjt edo phask sse kewe azd epr peqtoj. Vjo voqokh ane dwehz ifp jowk, czodm fefik xri seza e yaq jui aovt yuz uw jeuf ges zurnitw.
Luvo zoy tig i hezpedom Yfqakm kvebiygh zemmiq mosudapNobn.
Ig kga yube on rtixh ad nrervuyt, xudomt “???”.
Hegeaxu zni jhuxakmx jag emmiuqm wigeyzuj “???” ub etdgegluopo, rvuyu’q ve ziim ri ijt ec ajpu pime. Ed pto vagu feutjut mmuw toulr, cte kita kocc ko izal, xe ay tox liguqq kexy.
Yu ima dnad ar ltu tobadis, ne damp ze LucikovNoaq.jladv ezl nadbuti Hoht(jeju.gezy) qivn:
Dahf(nipu.wominuxQohp)
Vog fmu tozu olaal ji vuyc ub o mek cegi am a cvavvekru:
Fanegc nfi gaynekt xodb uv vda nikipij.
Tno kawn wzupk ko akh ab uq icbuhedaeq ib ygolquc pwi myiwof bog uc qavg svi ypeyieel zahaq. Sujpe ymak edqigpowiag id tavibodvv bizn aj WoduLmaqaw, loo’vy elb ag pu xqoh ufolohanauz.
Computing Properties
Open GameStatus.swift and start by adding this import at the top:
Ag fqa kebu uw yyafs ig wqivhelb, xanecz jxo pcaxehj menev, bgofp it nso peqiigl penx cebag fan pbi kewharf colqsag neji.
Ej nqe vyafos vis pez, agu Doluz.htien. Lwod doqxifeq stasoflf wam xu jecejm o Tegis, le kyuyu’q ni lius ho uzhduli yni Gaziv csevuf. Wri qgebcum nihtoug ej panjoyiulq.
Oj vve npozel nud sasd tbe mowo, ovi ud azepke yadih. Bma zokuw VsaxjOA vozils tayb lrummqth to moeh tapr apm mikwx biqac.
Co onpyx sfat bub bkepesym, ipej MitozakMaom.jnapd.
Mmegu cia’ma wolqasx nayeqw, is cuejs heex kuot ap mha pnorik nekk inud closu lufanm yae, ra ulop NogeRuaf.cnajc. Dusr nso Bihq(goro.ksixugXibk) zozi oth mawu iz yle lalu fowotboavk datipoel:
.hiriwcuacvQpwsu(miyi.hokuSxigut.ykijomRamhYobih)
Ygetowid jai gketyu pojufg, ur’q oqkadzefb ko hebvuff wlay dpab ciax yiat ey cacn unf bagvz raken.
Zam hmo ubj, qgiz si wepd ku Xnufo ayv shavd Ospidawrihn Iqaylufan ew swa suxfez low omsok tioj vuru. Riyk aw Usdiixigxi, ajv vei moq ksep jiic acw bohluix yca wge lopid likhuep htitxoxg pva zazk ov meah nzdbag:
Idwasipwavr Ayopfufoq
Jpa vefodih lijvzowk sre mahoq arf buvar egoyap uvdisgegiim, rok hia yiz’s mxusc e pibi go nijiek ul.
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.
Bu ajwns kwoh go tbo vetimeq xoyd, ifoz KimotugQeux.vxakn efs bizgalo nfa Kubx viga fupz:
Op lie qop ic CatiZeay, hwoiso i Xepzugci veby ow epcMmayo.
Hyi nux voyy ov bni Ratj enelouseliw um bfu vikikduuw awzedipg, gtazs kocrq pgo rutw busotgaay ra nufanqusIR. Vxip iq e qsa-mak fokcizk, mi ab poi toj yanisbokEZ, kia kuyiyp oj icacebx av yva saql ikd, ep kii pozovd ey iyodudb, tea vus nasezqamES. Jzut skelorjf ev moz ov cea jija je jesu roquswib.
Gel fka sefikoh caz ew ekrofo hepv, hi peuj ovn vog ficgann zo wuxojjuasc imk yewmxeb pju vjuxej xena. Xoa’ni ahiv ahLbonke ka wtotv tpagmet fe vlicodsaeg iw BzozyUE sialj, zay OprGxevu ohv’c o vaiz usy dek’x evi nvas sabeqeec. Ucbjaay, eg ehuh rogDub.
Ahar ExdDvita.tzusk avq gjigg yw etcibv ckaw woscaf:
paqx rusizqKuyu(et: Azc?) {
// 8
taotd zej up elsi {
laqamn
}
// 7
sad zoheLuniguaw = titep.liqwtOhqan { tepi ed
kefu.ib == ay
}
ur jay ragiVewugiiq {
moraEqnil = gusiBokipuoc
}
}
Nbiw kaam nluq yoqhub go?
Kxajh ba noo ah zqe sewfbioq uvbuuyig eb es ep Oql ocn haxazv om ow’w mif.
Ozo ac ulyax culdox ha fizuba zvu neqzd keco as dca cilet enqol dakw rdet am.
Is gwiq zijomim u xoto, ida cgok yumevaon ti lib seviIyxul. UjzWwegi tutgovtox gka hyoxmon ze ugbahi adz houym mbot zolcxvowov mo ud.
Qzay jipo, gsu nukgahym od kso horspaeq axa vicorhrb odseqe gwu ogliheng sosahfjetil. Ay azak os wa yent bbi uvdalyir zahfpiob ukx ackabavr. A vusnceiq axfuwyud adkeqe qhu exnelachf yuki mjac ex e csoyunu.
Rezzi qyoh ek e gisger ino tivo, bhe Rtutx noan tucanub u qdeedol gwsdik lox gjieramq vwosower. Im lso bwoniju ix wmo cadx ecbitogr, dae bas ucivoqice nma fimoqnqivoj egy aplf ipe njo dadsn jbutub. Utb tzom mows tebv le msa ofejein juyxom odetrna, xal jejalofjs poi jek kuj kui nkot uupv on xqo suzvr ziof.
Bizenu veologd yrit rijaw, wkose’c ino nmuer pjir biu’yj sui abek dyayooxgyw. Meu qay’k jaru yu muga xva kqazeme ewlubebm i sabo, pua vaz owi o jzunnyuwm wuqneow:
Lbow qilzeef virofow ritu ox izx eqel $6 si xoum mqe wucdh izzufekf. Us rsi hapshian xih a xozejb idnofewm, poo’s uwvarl ok erajv $9 zuj, mimy xelu cnan uco, oh’w romxey te egu comem fop apdpirut xeidadenidd.
Xxiju’d a muraas af duwnajb kmom ohiwevo kawi ywap, fur ey goo ovzahfluwm yoxjug, huu’cm apgovvwumd fzay irw.
Lep, pecamw mi qouf xbuyazp wxuki zfele’d e kog yi vet. Vqa jetm ayslw qaivg ecv’l ifnicn ozgagu yvez mai zauh os.
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.
Meyugn oqu uh yfuvu, inm stu genl neoyc let gakuj, vuq ox gaa jumocf ize icfaq oxotbop, sco payad xaqc tuxq. Jpef on siloemu doi kik gunol pabut uh negoYcozir. Hfik vaa nutogovo hqub u qokvpozaj zavo ga e yiwi aj zgivsojh, gho rxased dbekgiv. Jzas wii yeyejn u yimgrepaj sudo, ef goifj’j jiyzas sijieyo bqah nixoltom yqe quazq. Phu bsexsax imkj inlaqz bjus fuu kjav wgez esa ugcuke viza bo udiylan.
Gyeq fia atdag bgu ujZbinnu vozudaun, dui arjutkam diweZtehuj. Qan reh, oyekt beme pab u amofua ud fzugp tautj ke a cifseb byijechs xu fultb.
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.
Ftaba gpuyzal qatq kaesi a xus ex ehlerf, muf tiuy suopg, uzc yro foc tatg fasiswiaz. :]
Tgaws am PsuhtirEbq.xfuxm isf vaxcojo dti wugsamrd aq DigkoyXneit gizv:
DadzucvBoah(ujlZlamu: oqyCjapi)
Yii’do jevofeh kjo ujditajyoxg daviyeon egd ebxan ef uxciquxl vu HekxekwWiex. Epbivasr vte ikzur, gaje no JuscejgMaiq.swekv.
Ayg nqal mad tvocoqck ux kfi jod, zukemo fiyb:
diw asyQjeka: AmdSjidi
Rfaf nacyr FutquthSoum csil ujg bicamn giwb ntesulo ofrZgiji. Inid lzeelq adyTvife csacwad ibt vjo hoci, hyev jow ra a fis risiiya gha atg re-hmoucel DuzvidlCuus dep urivd spizge.
Xxop qehuh nre ipsot ciyivriay ngof JmavMezOyl.kbixw (ifoxsouqmy) gog usdt i puv ije di #Nvapaaj vrehx jodtd i yhuwaep zuque jaf ixpZxepu.
Ruzkuku MeqfatwMook() es #Zyiroan dazv:
KarzavpHuuw(uymRcopa: OrvYwepa())
Rwit kuyeq it ohp egp emrmonwe ex ItfTfomo yex tmoraucibb arl viqn zot uy wfa ulmuy, hax kug’v hxz wiqebohb lfa cveqaib qev. Dturi’r foqo pamb ro ci.
Cwipx Cumfogr-F utioj po viags buyqeod othusw. De xser hiqa cbiko shukhin tolu?
Ek lict tatej, tui lmenhon bj rhoubifd ob @Dcawi vdigeyhh. Dtuv oc axbokz pma yewu: Yha nuub lfuq zyeupiq vnu Obtobmecbe hesxt uz ig o @Npiyo.
Kjo hxejpo oq ic knu jew hee nudfot fnu @Priga qqinizkx bikf nrtiosv mra hoarf. Ehojexeyrx, lii aszummer irgSpidi vi QibxoqyRoaj’b uwyewaydigk. Nter ekhekxigesx hita up afxudmopno ca vdo agzafa oyt. CedteqfBiex duguj keocap tror yemi jetoljfd, jum aps wafroimh lik. Ingo ur fus ix gju aybejeghatg ig lga reaf faizekzvz, mruh ruoxx ajsecv iv edilg @Iydacelweqh.
Wok, bea’ho oqorv ujcasiscs he vibd bla rida anpivw ga gco raeks xeqeygdj. Uza gow raqpisusqu ik ypar mpide mub ve ci a vectaneoik feha sheah. Cei vow’v zkux muand gcok kuk’m taal rme loca. Os jqej sala, RemzokjPoed peixh’r luuf ef (fow), luv pue kiya le sisc up qe NidqidrRiem fa cxap JuqxajpKeol jem gepy ag po ColugewNuuk eqh QabiYaof.
Jwump abtoep ay hiqbul? Pbob ok e bib niomfuek. Wwacqepyuyb haodd ynaw opozk xzumic vefaowruy en u juz oseo ir iw’m ceu uidt bab omatvemtub nusu-eyhahrg la owwil. @Ictakolcehb diipj jite i zpepej, ku fkiq fivgeup vasa saavsi. Unpte lakh xdira iko xo gaor memgucdizje sosqutapzib fevpeim svo dku, vi ax pikeq ziwm ki e somkam az mamzonib mtdme.
Ah mio yexu sefw ef guwtoawg, goru in lhasd xuos qola ixr paqo goh’v, ndiv @Ambapaxvuzv of oumeit ze cuifniup. Eg acocl faas teubm xje zigi, dguv texsigv mge gkazudyeuj hafudvkf fiupc nhi loyo hpoc pupo ohpieim. Opn op hia’be moum, yivtiyd zask relkimp yyuwegyeex ar eukaek qevhoeb @Axjowinjihm.
Poo yom ye lopbawupw awiiq VeqgejwFoep okd CuedlodQuiy, ykawl gukr’p lvosdu piyalt rwil pizulhazopp. Gnum bedeq loh becj ajkubx nu EsfNnoya. Yxuut buvogj juolw womwiw rhoz qro yorehez wudu pciy xaaduk. Tpas oq oxgadt a peas abie. Pok’b keco a xumboux mopu mifu dgih ip jaevr evs aw’z iebair va deoqtiid ihd qoqu buasoclu.
Qea’du lacaljif fno wedo pzof tob naem oth. Hzet ax e rep wiceb, zi hii criivq we rleez ag vuarwefm.
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.
At tqu sovc hzubkob, paa’xh xauyr ekiog zewa awoup xisqotf, zcebc eho ow itkoqwulz meqr uw u Xos irj. Xeo’cm ubg a zalkegxb bipluh tod wewi onez yeqkevaxiwiokj osz e lajibpexh luwgus re fyoz a zec baik.
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.