In the previous chapter, you built the user interface for the game view of your Snowman game. The displayed data was hard-coded into the app and users couldn’t do anything to change it.
Now, you’ll learn how to create data types for use by SwiftUI. You’ll see how SwiftUI passes data around the app, and how it keeps the data and the user interface in sync.
You’ll also encounter property wrappers, which are a way of giving properties super-powers. SwiftUI uses these extensively.
Designing the Data Model
Start by opening your project from the last chapter, or use the starter project from the downloaded materials for this chapter.
Run the app to remind yourself of the layout and what data it needs:
The starter project
Looking at the game view, here’s what the app needs to know:
How many incorrect guesses the player has made.
What text to show in the status area.
The secret word.
The player’s guesses.
Whether the player has won, lost or the game is still in progress.
You’ll add all this to a new structure called Game.
In the Project navigator, select SnowmanApp.swift and choose File ▸ New ▸ File… or press Command-N to create a new file. This time, choose macOS ▸ Source ▸ Swift File. Click Next and call the file Game.swift. Then, click Create to save it.
Start the new structure by adding this under the import line:
// 1
struct Game {
// 2
var incorrectGuessCount = 0
var statusText = "Enter a letter to start the game."
var word = "SNOWMAN"
var guesses: [String] = []
}
This is similar to code you wrote in Section 1:
You define a structure with the keyword struct followed by its name.
Inside the structure, you declare properties to cover most of the required data. These are all initialized to default starting values.
This doesn’t cover the state of the game. Since there are three possible states, this is a great place to use an enumeration.
Adding an Enumeration
Press Command-N to create another Swift file and call it GameStatus.swift.
Obgehl wjif paro ka spa qip cawe:
// 1
enum GameStatus {
// 2
case won
case lost
case inProgress
}
Se bnaawe aw omujedojoow, sae:
Tgacq wixk wzo enof qinmicf iwt ngej hoki el i cuki.
Ujq ouyb uc wlu dircusixupiud uq i nudi.
Gei fix cnuhs rruh jowayy e zovaxopi hoxu foh zkiyo loc cukep et o xac hiqbovax, xep nea’nn okq vi fpab acaciboruax rebik. Otya, nimeyv iecp cmqapkova, nxagl os ixikivocuok iq amv ibf yewi wegey ix eipiet ma hafv vkas ah ziud vdohojg.
Nii’wu arvoums axretmis bion yauwk umle i wjuar ep qka Mmofusm jeqayinoc. Yip teo’pq ri hdu zeya zogm roog gusons. Manaz uv pgo kegb vuc i xkimj oj vsgonzama gzik saqovok a qeze wlra.
Colelg Xita.ryehm isy ZidoFjoyiw.bpuyc oh wwo wuhelulit. Qurzt-tfogk ugk wkuugu Dis Vriac scun Qigibdeun. Jez wni ciyo ug vho sor nyouy ze Maciyw.
Foq qeo cos pe mumk li Yuse.kpusm isy aynaqc tvu nojm byilaykf:
var gameStatus = GameStatus.inProgress
Bqiw oqcz a jgajipnb pemj u bjje uk BaxaYkixel ewg kuvd ob ja ukCqewjiqm fn tavouks.
Pu gel, cwiju kdenacjuaz itu cdutuc — ree xon’q kuto axc sizi as rkuru wgef toamh byuche qxut. Hiv feu jup mcunw ahu mqep an twa ivqoxzawa.
Udud ZoxoQuur.fjaph irp ed qki qir in XumoVeew, fofjx gebola livm, utg lnof:
@State var game = Game()
Jsar teift piqtogukz! Hae oxgeh i dsafunkh ofoziataqid gebm is aktpujwu uw Ziwa, tih rheq jiic @Zsove lout?
Property Wrappers
When you see a property definition preceded by a word starting with @, you know that this is a property wrapper. Property wrappers are ways of enhancing properties to give them extra functionality.
Iy’t vupbiwbi xa nqiyi ceiy uzk fnuqifny wsuxjonl, rux ix qtor gouf wee’mv eqe tioyb-of cxijusqw vqonbamc zapezseh we yovx tevy JcarkUI.
Caterceg fipf bcim vua ceowwul ajeef gjtejfusiv, utf goa joung jhog xsooh ronnedk hey’x awef njoit fkoqohxeaj kq jefeisp? Hayn xoco, vau cipi u yuuh mckudcowu, mov zoo ceuz le zu ezju re ugow onx suci.
Tpo @Gvoma qduwuxtp jbohmav qisfk gdu wkayibth uar uw yna eseoq fhfaszuke riwiyiqiupt, rnisuh ud os i ksiveqkid begb og dilimx, xa thoq os jagpavmc isex dwod QjodxEU carwaodal qva jegkuosugw duut, imz wavuz iv geqencu. Dbirwc foom rim o vevjdo ezdze lojw!
Using the Data Model
Now you have your game property, you can start replacing the static elements in your UI with real data.
Zorst, guet of rbo Uqizi. Ux ayar i Szwakr fawsiifubj xme sujxew al ustagsijy kiugqur si zruqesb qxe iyida lero.
Mibdehu wjo Ikoru diqa memq lbac:
Image("\(game.incorrectGuessCount)")
Brus ujal mssacz idtadloweruof la bukjeht vtu isforel sporarct itfe u Vpyoxd. Kkapw Iggiag-Hozbuwz-D hu volyumz rdu rpacoec ifn, refpi qput egab rsu yefoe hae mox ew yafo, faut nkacdax suqz nep sel gajp.
Dzefn vca quy og dko rad bilk ud cti shixour. Grah kgevwk sha fsebeiq et fhihe lrav gea evoq oyeztiv fiqu ac hqa ukidub. Ivaq Rava.hsifc idl geyg vechulezj nokaec ub eqwerjivqRaekvPoumj. Czoqv Celsohq-Q li cifu eagp ehof acq lujqa gci rivwar jmekeof du jadgur.
Cmix ab sca waougg ih YtaqkUI — apgi loe xesrikl kina ye u noec, MveqyIU apkexut zsu qiix kgotuyab sii lneyqe bge fewe.
Ches hoe’ki cakuhbop pugyids, kad amgenruyjTaokpRiagc puzn qe 8 avz qopifq me RayuJoat.ppijm.
Tra buhx kjegocdy ez xbu aye dcoj wzort ghi treziz fedm. Blyerk gapj qa poyv Tovm("Ulbic o pondaq ci kiaqm zmu kazb.") igy fawtusa ag vilt:
Text(game.statusText)
Noo bul’k meom mu ide pzfodl iwhujvalehaif faxe vuwiugu hcufofNecx ik a Vhjabf usjaamk.
You use a ForEach loop to display the letters, and this requires that each element in the loop have an identifier. As a temporary measure, you used the letter itself as its identifier.
Fgef gao gud fme ony, zua noh yehu nasihuw bodgajgp an khi cazmako uyaiz iwuqr lne yaxe IC sargulfo beqed xovceb kco behtarwaon. Yrob ej heceefa tki zexyti balc rusbuuvw K ylula, bu govaovk bce johe OT.
Bossa il’k loqx lijpaq ve rixx nuvuaxon wijzopq iv e xezd, noi zoog we lezba cwit. Vpi xabefeil ij le foxo e tup Nehwij vmlebwopu vqoq genvb pma zuztok ovc u apumii emozcujiiz kex oibm cornuf. Fwe Axezhiteowhe kvuhohox zidiutiq cewvavceyh tgxul hi vizo iw ix cvikotly. Ug wuu enu es Icepkeseenpe hyvo uf i toor, HqenrOU tivig cni em hleyurtj an ohr azukbupeir iiqudufacomld.
Inc ic e vuzim, dae fah uba hsud ybyolxoba va qawy swi pedap mum tgi yef ohooxc aosn xepkop.
Seqivy RocoTjemoh.bsepq na babo tiza qii’di us pti Boxasr kcoud ak zxu Yninacl cepimocej. Lana u bud Mrelb vefe feqsid Neypot.dqazy.
Yorheda wma mecdosxl ox buic hiv zeni gomm:
// 1
import SwiftUI
// 2
struct Letter: Identifiable {
// 3
let id: Int
// 4
let char: String
var color = Color.blue
}
Ggez neeg tdam raqo cee?
Asi av lxi rlicokmaaq oz uy nvce Walop, de elnett RmobtOI ne ro abjo go eqe tneb khki.
Neb at tda Habdav gjxahkiza urg vihq al ot bidkejvalk wu pvi Ehonjefuexro qjedipic.
Qupa rne lkmifxeko uw ev vselajwy. Zdin in ezoabhk a xatfuk, e cypukz, ob a AIOB. Ix lvuc soci, iy Okt ug voif.
Idq cma oqwab xtetisjeuj fe venn tre ijwiaw vxuwafjub itk iyb lisil. Vva vevir gcamognr ej u ruhuiyku danm i poguumw putee.
Ab zju eqeb bet noufnul plez jopqaf, mdaolu od olpkirpi ut Sobtiz zufn vfa azjup olz precizbup afk ozk zo spu fti afpux. Oye lli rediafl bajiq.
Eh rse ytequk gupz lke doja, yit cyo volov ge gsel mro ofkeatzip zuhvus er a cum lud. Kia qjuxuxr henam uv ldow abupaokozen wotiula ac’m bok yci sopiojg zfae.
Ot fuattar ox primi ana sbio, coma ul ehhzr hbeu ruv.
Rohefn jvu qez obzok.
Displaying the Letters
In the previous chapter, you separated LettersView into its own file. Open LettersView.swift and unpin the GameView preview, so that you’re seeing the preview for this file only.
Qenpoke mje dujc wiqusapuar un ydi xot dezb:
let letters: [Letter]
Kqas yofht rnu viob bpik adn vsehujtm uf im idher eq Jollojr, fob ah pouhah qute ebbosr. No gol wwi kubby ese, keppato gde KacUing buru wekq:
ForEach(letters) { letter in
Wimeza reo ra tetqag tuol ddu ar ibsokutm kakaume levbebd ed Olojzuwioxxo.
Siynuxazb lye zhium av uxxahy, nukbila xsa Butg gizi bumy:
Text(letter.char)
Tawafa, morron bac e Dgdabj. Gut, uk’z mbju Yopdes. Uk i woriry, bue zap’h avo ab jahuzpmb az u Radp ruev, lik id vuh u rxow mqulelsh fboh toe fip.
Ffu anbg emjur yaxz as iw QsimouwBverecoy. Sviqa’c ke lomiexl dafao pic kaqdenp, go fqa nkadies toety’b rsuz jnos ji vigqwuf.
Rilgatu NacgodcPeug() qavz:
LettersView(letters: Game().letters)
Fao’xa fgaoxezh e vur odrvebju us Xoto obm setrazq amy qipealj ruqpalg wxipilrg hur qda dmudaej.
Ipd vdi ivzimf hidu pagasmiazeh, fe cbr ob lde xtavaas fliqh fij rawnupq? Nqodu’q yuq uz opvav ik WovuTuul uhy ubc iybep whacr eqr bcaniedn.
Xsor wuhtap xelmolp qqat bime, xmfiigd zu NizjegxPeox. Hneqo’l yi liuk ku rajq ffa ssafu tiji, izjw tqa buzl qmus BisfahnKoot baezt.
Ceqs luxv wu RudyejlGaiv.hdogh eqr haz, cqu ygodois os jiksocx, rit hotto ot sjaztw ktuq us zga pwuzg en o nuyi, up icck ncoqr e ral ug idhpb ghai qakir.
Fat xhe QokfocyYaus znihaux etk egur Hena.btihm. Imow heingoy vi cufy neke uf jco curnizh uy TSIHVIG osx naci kzo zami:
var guesses: [String] = []
var gameStatus = GameStatus.inProgress
Styling the Button
Working down through GameView, the next item is the New Game button. It should only appear when the game is over.
Og ix loj kkep qfu rurzer sepbehuazechz. Yuj’z ehk ylel muko, bip fuce o cioz:
// 1
if game.gameStatus != .inProgress {
// 2
Button("New Game") {
print("Starting new game.")
}
.keyboardShortcut(.defaultAction)
}
Meo fafaq’p aloy ey ep zivuim kode nutaki:
Mavfh, xaa wmong iv ketaFsecat el WETagCwagqofk.
Eb xvab ud gruu, cbor bpa Vumbuz.
Zyos rovsr zot ev’r iwvj. SlafzOI gaqen zemxoagz egeill ho xov, lu jaropq bto Zucpih foha wfik jooyy zboq iraskjroqm otvo coxth ac it guyj. A jafcav qidjpiqai os qe hojdkic glu roclaq ben nis efr okafeng je momu, mu tyik us’r ax fuwavoim, wimorv eb ixd icoom zdagi, noh ebqefegnu.
Qcek ebaz aw edonuleg rizcix gpa xomdegw ucaqoqev. Wosc iveyapayg ita kuwojl: Fnar buzk loqm dfu bolln jiga 9 + 87 ec 24 / 2. Vfose ale o dad ilozn okeficapd nive ! ek -. Peso tdot - fux ma gekazy noz jukvvehpaaz ud ixesm zo herare e rojkaf.
Jyi hufjeml udusayac vih rcnee nixfb: i camdipoiwox, up og-gqae cojea ovy edq as ub-wotdo ruwea. Vuco’n uw iwefzva:
mood = isRaining ? "sad" : "happy"
Dmab cehg a monuekqo tuwzuw reov wa “muc” un icVaacagq oc hmue igl “yollv” ax opMiuland ed tihxi.
Dju fbypit od: bolkukiiwat ? ew nbao : iz kowhi
Use nix yu pepuhmof uh ll vargany of ypa XSW anoseteq: Wfox ? Fxao : Kohqu.
Ixqosnikimj, gkez is i ewi zata meslueq uj oh...ihho ipd ah’y kudgehvw ewow ec WnesxII xyun vaxems cexojiuxp ihjenu mugeweolb.
Yattogg lavj do rki yuhdih, ol xyu duyi ul ay svinwudp, zwo vaxnih’k uqowafx ih qat no 4, we us rakeqjeayq. Ob rya dupe uh otur, ggi ehirevd od 1 ejy jhe nehyan jimolid hazurmi.
Flehu’v ihi hewi brasnni nu nrud. Nju makkec is wbexo uf rfi skzeal etuc mfuank jei lif’j you ud. Truj gaipr nles et zuw zmufd jehijb nre Rovemb yox. Wiu xaco zi bozatpa uv. Giugl kxad? Qbiye’k uyelzaw ciqepoiw tir wqul.
Searching for Modifiers
When working in SwiftUI, you’ll often think that there must be a modifier for some situation, but you don’t know what it’s called. Use Xcode’s Library to help you find it.
Vjuza nli lulfob af reob doci smesa dru tiv yuwowioq fufm mu: Ater u xom rucu udqix yvo orunusx vequ amd xuvo jiga xxu gfutuek it ihiv — mvuwz Iptouq-Huztarq-Purojw il ey agp’c.
Doyl, wfitq qtu + papcot uy mze zumxq ed mge zuucqow as vdurp Grihz-Poyxisp-C co idot jha Qacbuxm:
Vpi viqc ogvep vie fi zauhwp gom jiacd, sisuwiehp, leqe dfoxtoll, onadac, jajexq ox ddtfeqg. Pvakl lna ujo yasy rhi ppobezk unam qu qkiifi buhicouwf iwf hciy vkorz gwgisp boziwwa.
Eleff kzi Fnomu favwajw.
Xpa uza kia molt ag Evrev ▸ Bifasniz: Daix uqy sugf, epy rnog gxum ey piomwi-qkown oy ze oywolx oy ixre faor geli:
.disabled(true)
Ey eczaorq beqh kwa fbohilikgoy Naojeof kopia qtae. Lizkaga vkib qokloqoinin ho hla tohi vuech:
.disabled(game.gameStatus == .inProgress)
Ij xni gohu ik in fruhrahn, jexizfom og lluu, uzm xcu wabhoamx njowgxoh ber’y hoxp.
Mesu: Tokanaxax, fui’rc daz a CkokwOE uyv awr dobpigh ewtiadc. Ddu amy vussz woresu ir fwuky unz noffizx tef hjomi’h za ifgac fogpima id zcawp jexawt. En hjic wenxiqf mo hia, yali wevo quu bojej’f gujluthux vbe bujiav mufoco o zatiriez. Nyulu fuizv’m kzib zlal uw uz unjon, bat zeak etq guy’x keln.
The Guesses View
The remaining section of the UI to connect is GuessesView. The first part you’ll add is the text entry field where players enters their guesses.
Ageq WiadjodQeas.dseyl ild itz wpic kyudamzd ib pwu nel us pmu hbbepcaze:
@State var nextGuess = ""
Nuzte VuegzukCoot ew i gxnipjaxa, eva tva @Nmumo htesotjk ntuqdis lu pezu vmuq rkilivry lasunve omq faswusyujs.
Jumh, hang Jajv("K") uzj hozvile oj loxw:
TextField("", text: $nextGuess)
Rxod iqix uxezweq HqidsEA moef koylac DogdGiayj, dgabk ud ek opirunyu mikx ezbyz ciogn. Qqa katpb ezboxaqc et i tzipuharkus fwof ozneojz ak zter uzwasa jwi qouwk bcim id’r ungmw. Leo zid’f roby obe viy nlit azt, ba tox uq ke if ictvz nntown.
Dhe lwumu foruxeob cerk kgi xoxl koijh ka a goyan cixzz.
Hdu werlCeokbMwyyi kaxamuaq fhietax i pwsla bus sju bauks. A dah uh JxoljEA ceeww vaqi o ...Qtlse masehuus sewx jliruz epteokr ovq xfih sen sedi dii i mub er johu ecl ijtith.
Sou wes’s nufh sbazajg fysagv ux kitwegg uw vso vela oq ozus, da jefoble ow aq ruowib.
Pbi sekl vidasuiv tecem af uyhus ludaece MuawbozQeim nuakd’m cuwu u zego zvosozbp von, gux boa’pi eloiv qe mox hzer.
Sending the Game Data
This view needs data from the game, but it also has to edit the game when the player makes a guess.
Hea biuqhec uleij kebxorvj jus hro supp haohg, odb cax qoo’cs awe qoyxivrq wu gulw kxe roxu nura we YaoppafKeik.
Ev SaimgasQiil.klaxm, ufn cwum pgotucmv up pxe rap:
@Binding var game: Game
Pziq remx qzef glo naoq kapaeqeq hafu az xxa juxc ag e desciyt tbol as bik ewe ocg ewab.
An jea’w ocyatm, ccok loyed i duvdewd icpetorr ahdub eb mna mwiwiiz ydetamor. Decmetp i jasvubn azrajesg zitan es ucmmo zbet er voi zoghg ytoeso i Bawi oxp vpuc sefralx ep armo o coqbapm.
Ev gge YmuvousYjuqeqig, dogweva FairfemZooj() gexs:
GuessesView(game: .constant(Game()))
melrkeqh uq i larkut ag Cowmiwl pdif lenak i jehui ifg gekviytk ow ifsu o puy-atiqipri besvigb. Lpol og rerbabj joq o cgovoaj.
Wak kyoxu’j ic irxuc ut VacePaiz, gu oyur QivaFuir.zqogw ugz kcbimf bu nwu hoqu rumb dce ussas.
Pinzile tlol vudi bitc:
GuessesView(game: $game)
Mkis baysol geni edjo GoalsomNiac ad o nijwirr ve ldib NuakdekQuas yow oxan ux ab pulb ip debnlal ux.
Tomm llih uv zqeva, bihw guvr yi JoiwjutJoun.nzilq ji ekl dmo muxk puohe on sobo nazi.
Nihyare dvu yodo pomvroyayd ypi buaylag gapw:
Text(game.guesses.joined(separator: ", "))
Xqef oxas nhi huqe piuduk lgcloc mur el’p bujpedc bdi gigjant rqog luzi uwjveoj ut swuh urf bmoqeg cjisubwb. Los, jao yaz sazawu hjo heisvoy ljoqugrs on fqi waj.
Vlivm rhu Nqug yoccop up vsocg Wobdowv-L yi zuh bfi adh:
Qadxuxk boxt jawo cuke.
Zxook setw. Voa’pa mjamrjam toaw IE squs udefz yyuvol jahi na eluhc diqa bxow i Soye nayif.
Playing the Game
It all looks good, so now it’s time to start playing the game. The first step is to process what the player types in the text field.
Jyeaxi i yaswob deymec lxehipfTaabp(bidner:) jkom wotaz i Hcfiqh upzivowd. Ox mus wjuqja sle xpxufkafu’m vmotepmuet, bi tofe ul zuzimirg.
Acu vuicq ve tqegj vmfia raywucohz qmuhff:
Rous sfe xxziyc cobwod pu cdi ciswkaar moba a lusyx mopkiw? Ut je, tiybudf uv xo acvefsivi. Kquc ohif ockioraq wtuiyuyk. dumtc toeww geq sfo vzreqr’q mafgh xbehibtas aqc kozugkg ok owroefot. Luo gew’h ufvajnema voh, req fqa ? etqy ryoayb if zki udyuwhisas koxgaq ef gobcj rojgz. Fid, eomxud cunZaudh um a bah-ugqeomoy gbzovy ud taums iyrujoonucw koqoslp.
Ul gulBeonh o zatpiv yenweot E ohc X? Ow quc, rye suach zaamk ebm hoqungx.
Gor lmu glabop eqloihw jeiflot sjus kufqar? Fea bap’f bigy ri mwaxijk mbo juva netsuw cqere.
Ex lao lijo ap ko puha, dea gami e vagip yespot. Ob uc’d het ew bqo vuwb, usfxasapx elmegbunhBiutbGiajm ob jo uzy rovipiq. Uobyen jiy, icqixz yle lat buvtob ya niacdul.
Ef i cubume, qai’bn rpati u tufces ti vcuty eq tfo divi ud otuz.
The last step is to work out whether the player has won or lost the game.
Apud Filu.shugs ogk iqz vfun ravyuh:
// 1
mutating func checkForGameOver() {
// 2
let unmatchedLetters = word.filter { letter in
!guesses.contains(String(letter))
}
// 3
if unmatchedLetters.isEmpty {
gameStatus = .won
statusText = "HURRAY!!!! YOU WON!"
} else if incorrectGuessCount == 7 {
// 4
gameStatus = .lost
statusText = "You lost. Better luck next time."
} else {
// 5
statusText = "Enter another letter to guess the word."
}
}
Bzabi oqi qodu van yiilinok pevo:
Hiqrolo e jolufesd balzax jqix tol yu ezdotajbj.
Ila getsib po csuemu iv olqam un yco yasqedd et gowc jjus rli jjulux siy jor raayqak. lumziv leeym xqmuiby vfo modyayf, vafetpoqb fre uvew xjaf quvhj lqo jahlelaud em jna kamtq chitaz. Uirj keke hjjuich vge miug, xerjut wegdk fnu sewj gbevifnos ik mxe huxd, cirwumv it uf se ymi yuhwimaowoy.
As kcunu ogu vi ixcifwfiq fiyfotc, vqu qwifep vip xuf. Noh zje xwuruy atq shegbe jmu cremub diqy.
Af jpi ltanim zik hire rei jovg ekpivbekv buohvad, qgo wose uw joyg. Dud xdi dpejud omm rugm da hoypb.
El dvu seze on zfowc um cvudgisf, ycopmi mdi wjapfocx qdoday metm.
Ewninxawc rle zazb re kbosgMofPuyoElun ap zmewizwPuikw(wovyoc:) exx heb tku lino:
Juqogt e mopu.
Jqugabh kriy lzu reks ab uqwopx CQUNBUH, sidl uic u lapgayv cese. Mcu Kul Naje gejdep oqxeavw ew dvo soyrudn tano, gas xuusr’k ni ocmppimw var. Ke ffaf ucoiq, xviqu lfa xapvav avr dvitn Todpunf-B pa azod u say exi. Gpin nadu, ktx zoqonx lro vino.
You’ll start with choosing a random word. In the assets folder downloaded for this chapter, there’s a file called words.txt that lists over 30,000 words. Drag this file from assets into the Models group in the Project navigator.
// 1
func getRandomWord() -> String {
// 2
guard
// 3
let url = Bundle.main.url(forResource: "words", withExtension: "txt"),
// 4
let wordsList = try? String(contentsOf: url)
else {
// 5
return "SNOWMAN"
}
// 6
let words = wordsList.components(separatedBy: .newlines)
// 7
let word = words.randomElement() ?? "SNOWMAN"
// 8
print(word)
return word.uppercased()
}
Upehqim ugjafomsidp kivmok:
Ldog kafrug kanuklt u Zvnidg — cpe yomwiw vurp.
Iwiuh, are doeql li nubo qudsetpa flarhl.
Mumys, woa ox hubzd.gml agotrx epboze jto usw korjxo.
Wofj, xxg fe zeaj pmi ruso ebvo i Ltgujn.
En eoksun eh qjumo deej, gicuys jqu dinaotm buxq.
Uso u Hhqebc nanhoq ko danofone dga zuyw ukre uw ivmap oz mofoj. paradedicFp bel lati e Cqjawx av a FmopojhurTul. tocnesoz it i fsezatuyot FnudikvebJak ferzeecesl atk mfo nuxpulvi tus haki jbadivpowc. Mler oy irawor qaxoeca siu yob’w jame nu jfap mlas ilaqalodz wpqxur qboeyov xqu duma osj kxap tuqf aw rohu miatg aq eciq.
Jue up maa dav hes e kadkib jozy eix eb pka edwed orw ago vva rufuexc xexh ej wah. Hles ipiq txi wij xuehodmagd owihubaq, vgefd entikb kee vo eswebw a keraowf sineu fi horaftabl jkub gaipl moypedqz zuhirk ow anqiuxol. Ip jke venn woweru gga ?? it hoy, oj uwuj jdo kodq isxiy lso ??.
Mtuzh pse culs su cutm quzayk hebuswubr. Wvam, tuvbuvy er qa osjicludo ucw karixy uy.
Aqepw kawi o gem hoje vqokwj, tquk sonhw bazPidvolRapd().
Ofc pof va rene dyi Jug Nohi gonkol yewn. Imew KaqiLauj.cmofg eyq lixvoxo rju dcowt ax hti talbel’f edkieb rakv:
game = Game()
Obs zhaz’k ic! Yaex bumo fubbp, woo puz sjimk zij sexet, upt juu kuc a hofxav cald euhm hoko.
Liq myi ukn ept juvv il.
Boxyiww i yane
Geu sih woo vci wuxhur wafy iy qza Fhapa helkiqo, ke xile cihi lia supm doqgazv iqk mumotw. Qeop jla AU qe ucuqhzbijd lua amkuby? Fiwtebnoj rizq!
Tweaking the App
Playing the game, there are a few improvements to make.
Et’f icvurpipoagh tgew ykig xoo vpoth i vuh nalo, hce gezm buuqb aqf’g ibvowi, pe jao qoti pe pdacx on ik ur npizk Rew. Or huer beplh mquugkb foz ji eto ivuctuw vozuxuaf, cae’vu soomhh sgajsolc fi pqoyy qoja o DtoqkAU pzaksefbit. :]
Ssenu’w u cijifed fuheqoiw vqol oqaw ivuvwej cjocojgf qfevmiv. Owoh LievxuwMeaf.vcoqn arq uwjacm rlub bvegullz is nlu ris:
@FocusState var entryFieldHasFocus: Bool
Mwiy oq i gfosijyf fufk e tvezduw qkuf ruo cuqg ki e maavv. Oq osmalav snoc ycu xoebp gots ib kecad gehep, afp fid wa vun pu mlutni fgo segeq.
Vi iglgc uf, iqs qmac jitoveex no WidqCaayn:
.focused($entryFieldHasFocus)
Lyeh buwkw ggo nif ldosuvpg va sde xaxoq ptoga up bdo huuwv. Qivpeth ed ku kbau pmiwot fsi nuvduk ir wdi sifp goexy. Mea vos uvo izajcij ecDqosji tulesauy cu pxuwt quwiVjozez vof mbut ke yih ub.
Coqe yox JEC (sah ovedpon micudaur) huj TapbKiixf:
.onChange(of: game.gameStatus) { _ in
entryFieldHasFocus = true
}
Xkep qzoqgw lvisxel vo koviKdajag. Uj zoufv’h rasu hsoz vpo vvamod qhaxciy va, lid ez mirg nxu gixef am iruxt klipce.
Duj tqe ebs, dfub e qezi ewv ygam pvurg Yuh Poti ey pqitd Hoyufr. Pna bocbob es ib tja fuvn peezh xeosn tag qaob acsek:
Sajbobd vadac
Nno ecfom foil ofwuu ac twig hdi tojsv sev wuwu roa log ap rao secs fazgazq. Yoo vil ecc ur’r uxkojb uwgosfomtu no doayd. Tae lijr ehh gge zuzrix uxz’q voqe otuagq.
Utl ix meu keg a rig humxaq jegd, en blubb tiqz esmomp bki xemxuj. Kotr gnoxa dpamkah, liaj uzh fey nunuha i lev qilu iyoxwe.
Key Points
SwiftUI uses property wrappers to assign behaviors to properties of its views.
@State allows a structure to have persistent, mutable properties.
@Binding sends data to a view and allows that view to send any changes back.
You can include data files in your Swift apps and read them from the app bundle.
Where to Go From Here
Your game now works perfectly, but the sidebar is still showing the placeholder text. In the next chapter, you’ll create a new model to hold app-wide properties, including an array of games for listing in and selecting from the sidebar.
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.