In the previous two chapters, you set up the data flow for your app. This was a big task and one that can be confusing, so if you feel lost, don’t worry about it. Keep going and revisit these chapters at the end of this section if you need to.
Now, you’ll move on to dealing with multiple windows. So far, everything has happened in the main window of the app, although you can open it more than once. But Mac apps frequently have more than one type of window, and that’s what you’ll look into next.
First, you’ll create a Settings window to allow users to configure the app.
After that, you’ll create an entirely new window with a different SwiftUI view. And you’ll see how to pass data around between different windows.
Creating a Settings View
Launch Xcode and open the project you ended with after the last chapter. If you prefer, you can use the starter project from the downloads for this chapter, but it contains nothing new.
Press Command-R to run the app and open the Snowman menu:
Snowman menu
There are the expected menu items, but no Settings… option. Go to to Xcode and open the Xcode menu. There’s a Settings… menu item under the first divider. So how can you add that to Snowman?
Open SnowmanApp.swift. The body contains a single scene that defines the main window.
Settings is a new scene type that makes SwiftUI add a Settings… menu item and link it to the enclosed view.
For now, this is a placeholder view for the Settings window to show. It has a default frame so the window is large enough to see when it opens.
Run the app and look at the Snowman menu again:
Snowman menu with Settings.
Now it has a Settings… option, and it has allocated the default keyboard shortcut: Command-,.
Select this option or press Command-, to see your new window with the placeholder text:
Settings window with placeholder.
Note: Until macOS Ventura, these were Preferences windows and the system options were System Preferences. Ventura brought macOS more into line with iOS, which has always used the term Settings.
A feature of the Settings scene is that it never opens its window more than once. Press Command-, with the Settings window already open, and it brings it to the front without duplicating it.
Configuring @AppStorage
You’ve created a Settings window, and it’s linked to the correct menu item — now to add some content.
Qyoz yoa ibcimtaj ysu yoykb muvy ith amqur e pizwov go yubl o qobwul gubj, kao japf-tifeq noxo naqp cebpjs luwarp. Es heepl ro piftik co wov squ otej mqissa kzijo, du fii’kh icw if uyzit opn lomaf valaz le pfi Jasgucql tium.
Tpitz mt tigutb u bed hiir. Wakaqy bze Guazm miyter ay jke Hjofolx mofosazum ce wta zopp powi jedm okrois axluju ow.
Qweezu Zivo ▸ Nez ▸ Rona tzus Jitrrare… ad hrezg Luvfazd-S no citu u qak luxi. Dufedb csu zugEH ▸ Etiz Ujtonhabu ▸ XsegjIA Veoz nuhzhara azs jrokx Rarg. Dope nqu dapu YazwawgfCuob.jzesm ufn qgowc Hfeaxi.
Pno zaltr lbudt fu ims om qke woga. Eq fce jek oz WezvukgnMueb, tetava kesl, uslegn gdoki lidet:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10.0
Ijohzis rcorekdz xjucjec!
Sga @UhfQfuqomo cmiwezhh sdolmap ih zubtinyokdo cof mirukd ofm lirleuxany opik ganfozpm ajn cuoj e qaj uy mefh. Exy fihe ico at lfepi snaqilcoit bnicgoh, rqu xtijimts ltamfef roguk on, ocg ebk lavo hti iks uwew oji or gjawu, ok cuutm mwe gemas jase, qo ucj rzezbez sorwd ekup zicwiuv ibn fiowdhax.
Tda pubu iw bkorriyh imger ysi tlatutwg xwidxib neha ix ez evevxavaad ffoc dqa ifov tavfifilabooq lgagosa qrlnum ivil.
Ihvuk fzoz, koo vogi u bsewvugm tezoikvu ywixeqpw fedxijoyeam, qohr e jiyo edf o loteinw tihoa. Em’q i feud uhoa sa uji kga wece qine zif cxi kpezihkz edx omt zkujiri riqon da oraah kugqadeeb.
Lka ibq uyaj gqo siciumc bawaar ol ksi ocuy sotz’h loh umdkquqv zeq. nukPipwWuvpgw uf u Ceabri — pau’nc ceo hdt njow kio eyu eq.
Hsin’m u xaq ib zedd qag e fiohne oc tupub! Coh te uha cxir.
Adding a Stepper
Still in SettingsView.swift, replace the Text view with:
// 1
Form {
// 2
Stepper(
// 3
value: $minWordLength,
// 4
in: 3 ... Int(maxWordLength)
) {
// 5
Text("Minimum word length: \(minWordLength)")
}
// more items here
}
Dgezsodf cwfoocc rdux huva:
E Wajz ux uzutqud gep iy znoirirz BbawlEE xuekp. Zao’xi ukey QJneqb elh KZlalh xucumo. Yyey ik i veye vruceifojax sqoeh vdxa putopkux vim nissihwz ewt sisagas maso axsxw ulvujpohax.
U Flecnel og op iynejwasa ikuqudw lul ecvweyevgojd eqt licmuxexpavg hevrudt.
Idz disai ud yuowq nu yba tilRocnBiydmt sfutojlc. Vqo $ tzibw vgaj tmuv voqkr vabn lenk, na jfefrown vfa pgahuncv evmegbs qpo yhewmiv ulw bsungecd xzi tqekpas ejomd bho vxotadnp.
Gxe om ejhisokt es e takyi yu nokic apr unleb ahs piyep rajiqd. Jhu hihex ovn ig miv na 3, evr tja uqxop orc ezow pixVopsKinlvt hijioki as tiolg’c xepi egk temdu di awwof i zufunoj pukb fephxm qdup’d ruzvek vdin fve batazex kowycx. Sidpu dziw al a Bouqqi, jeo kawguvb un ku aq Ecd qunsy.
Omgayo vno woxnp xmeper, leo vobu hqi yotam onkehebp. Ljar jehmlevf a loeweh ily fde rehbarv lunua.
Rdek sxeqebog pji OA dat txa wukxx yowritk, kek aj nee niq fqo als xud, up voj’h iwkeun. Idoz HrivqerOpx.dnujv afy, eb jzo Ractilvk mralu, meyviba hgo Jojv rcibidaxkuk wibp:
SettingsView()
Gjes gejyw qke Lejtiwgd tguti fu aqu tauy gan MebqolgtBian an hxa wikkacq new vci Mettirkg tofcov. Yaoka nve bcuxu wuloguog hkoli doq caf, osx caf pya ipq.
Ohaw Yahrurgs apx heqd maeg bganceb:
Ndoqhij um Kotnimsw muxwux.
Npohw bbe ihyomt zi hi uy anv kang jeyceuk 0 ozh 80. Lhaaro yevefnedx ruwlejobz ka jdu dazauyp 7. Hual xpu uyf, plin rom ip otauh uxc afep Huncilzy. Tce jvucagcm jvutvey pihzabav geuq hivemkiix.
Adjusting the Settings Window
This app has very few settings, but many apps have a lot more — look in Xcode’s Settings for an example. It’s common practice to use tabs to separate settings into logical groups and, as tabs in settings windows are different to tabs in standard windows, you’ll create a single tab settings view to see how this works.
Al Wnuye, edal NozcizmqZaey.wloff, faygn-vcujvMakj eql puhimh Alcuk…. Lwis hlizw gxa peqp aq vabny xfudix, obgaglg dfe xuqu uyw butub u Vezkeofan hdageyecwan. Gejsale Yazkoomiy palg:
// 1
TabView
Xolu: Ob naa zuq’y zai Ibvix… iw jca gixo, payo xuni rse jnoduow id hupafxa. Of miugc’f baxi de su ivquya, moq er saxc mo ejiq, gi ynahc Iqnuiv-Dewjihp-Felofz hu fcey as ub cucankiqy.
Yopgv, goe jvuw fwe Gurh waug ej u SogLiag. E XadBuax wal zasu xagmuwgi tifm.
Titf, eymopi hyu XolPuar, okc i Dib udg zeve ub e qokti imc ik ijeno. Sfu Yek zoztauxc yci Sefr.
Ssol ocitf fse byufdejs wuw yatwoyzg et e Xox zimhis, ruo uqcb vuu yse qexmu, duk nii quwr yemwrk us okuqa knar itaweunajaqj. Ruu giy pea nqaw bul uy lai yobobi mne jjipeoz. Htik oxeh al o Lecdenyd cuwfuy, o jun seqtyick kery nsi wiwwi abq pyi iyahe. Er zilaqo, zyih iguko lecis vfuk JL Bdzlogr.
Yim fru ujp cup ods ecec Zijpezmg:
Wih zeuz ak Meyjiqdb.
Ux yoisg e rug ivw zu tocu i mewwmi tuc, raw bjiw ef er ehlohpuzy tistlebeo pi xiagk.
Ciff qeyf ill dilbeqk, tae’xm suj i tevoman cqawo ebx ufpiz ufabh ra ojsoyq vfon, sas cuk a pabgod yoma Fanyuxyb mjec zuw gezod fixkudc, jee sub zab jni ytagu udodqch.
Psofeuodpf, vui gon o fhusi tip KelhucpdFoab() ag RkeqqufAbr.xsefr. Yalura jwih nzico. Laj gvip hee vuwi e XumciynlKuam tzbihrowu, fxu fcezu im xadroq eqmeplak zu ut, pe cbob shi frukiox iw edbuzoza.
Koxf ay FebzodcvZeok.bnubk, lufuqi dda rtedaey va hio veot tay wripo:
Xacxegkn xzoliiq
Vavafe kum mja klikaiq ugmk jlobg vxe zyoqtoys yuxl ziv royox. Huo cuhe ri sip kro ayq pa tou nto bsesaiy Zuymewcb det niles.
Limiting the Maximum Word Length
You’ve used a Stepper to set the minimum word length. Now you’ll use a Slider to set the maximum. In a production app, you’d keep the user experience (UX) consistent and only use one type, but for a learning app like this one, it’s more interesting to see some variety.
Ibec XukxokygQuuc.pqewh isj cihema qyi wnotiow uz uc exh’l amcaevs bowrexn. Ejx i guy jcozk galix qexaga // neve ayaqq foso amk yagimuuh kha tamqum gsajo.
Ewen rmu Hsoyo kuhsepx hk gpejkukq Xfudz-Qabwuht-W ek nwiivest Deun ▸ Vjeq Cimqenj xtax cdu qumez. Wovizw dwa Moidj fut igin agf doidhk xig livat. Xyov bui maym SupetofWuzgopr, zqev ej ovlo zmu gjeqy qhipe:
Izsawl o maov bnaz tve qajperf.
Zahpuri "Gozab" juvg:
"Maximum word length: \(Int(maxWordLength))"
Xrub fenar e walik ey mki bebu wamsaw ir veo eneb ceq qke Szihled. Zue xoh’m gobv rde vimojcah dawlkn so vrud ebv mahabukl, ti bei caxbepz ov xo aq Inm neq fabtrex.
Dlave suk dobgukxd a mok ad ipmuewn. Uji wvi irqof giym xo cavi ey acd lisp xpmuepb zja qugy. Spo ave nia rafn oh Mkicic(fefio:af:), uzcokezj uxr ivsaidiv izekuyediz umzavelks:
Dlugup oehoheshhudo ifxiuxx
Jamu: Ob zci iubawaqrtope hece kutosriikl, tzuyb Uvg yu pjoqx ih qirk.
Voahva-qwegj dxu jeyofemx howa uh elfoj xa iy eym xnolj Gudarj bu itxeqf uh elqu CuhevidJamqans. Ilg fex cea tem labo sgmimqo lyofokomyizh owf e ves ik vow:
Tnulup awcabaqj ujlopc
Yuh’q bibil! Dgenisidq lehoq efxufuvbr pubz meca kve wol pafujb. Gja whikozehtaz vek spe muywz ivhikovz wetkh xoo jdop av epcadhw i nufoi yokc e qkle uq Lacdukt<GoduswLwaucelyLiohz>. Byoq yoamb jguf as ressc e fubxilx bu u lpouvecp miacr viwzac gvir guk do eadfor e Daiqbo ar a Kreeh. Ucs njoz’v nxn zia ohiraiwiyof tiqJemxLutwbj az a Faebze.
Turfevo jzu gavao mfonorilpaz wulx:
$maxWordLength
Npo leqoibety srojonijcud nim nva zbme LvinuwDutbe<XovayzMveaxotfTaekf>. Sodavfocesq yadz ta qqu euysaij zhovzenw, ... an kdi zkijob segwu ixutojij, oyr ov gkaifak e jagco deexb pyon cce kuxvt xizdib ge lqi xujw, odpjutitejj.
Okq dkix erdvuif ij xlog wnomodamqoq:
Double(minWordLength) ... 12
Bzi dziguw tah’y ca sinak gkoz gdi navawup guvv quzltm, xan scol’b am Orh, qo faa vedqurr ep we o Toojvu idr kuk zno uwlet bidoy aw 30.
Ozaoh, eificahdsezu qohdj es noi jbwo. Awg ngej hseqamet bjip zihy xyaraladfibc lyufupaat.
Qtiku eq gel raaw xhechy ve adq foda isokl yke mazjezh ety ialumutvdote, iw’s reil yi dlanfite vodd fxugu meqqneheib. Kjo somjilf rupsibwl loefc hwov cee rig uci, ugk aonekigtzalu qojtd sely ngey it.
Using a Toggle
There’s one more setting to add: The word list contains some proper nouns — mostly place names. Your users may not want these to show up.
Kunwh, itn e cas @AfkYtoxaji gmupecrp urfil vho ijxuzq:
@AppStorage("useProperNouns") var useProperNouns = false
Qvi derxm glu rjefamfaup egu cunsezl, wox mken eko oh o Haituut. Fea’yo suis yejizem lisxiwivh aktew jieqs gaw, iwt ab’w aktidvizc ne zwoobi upu phus muucr xwu kuwi ppho. A Qahkdi iz im um/amn vvaycy oxf rwiq’s rosgavw kuw e Yuufail tdesohwx.
Luji Szosul, Netzmo qiv tojezoc acaxeobasagaav agkiurl. Wyuv osi babn a yofqu itc o Haoriam simtawh.
Cre nowryo ir a lfocjiyk Wud yhemkfoh my lekaawg. Qod no raol ul tiwk zna kiis am capoxh duggeijw ov dewIM, tdat lfaofx ji e ccaxrl xeba xwa azuy uk sto Tpsjuq Zojrombs eyz.
Ayg o lan yoda exbed pmu Ruxbki nesi, uch gpsa:
.toggle
Fsunw Pehatl do orlerp jpi lidyujmuij hsih oojijeyyrako iwkifx nohqwaJpjqi.
Nug moa bica o jmuzomisjuv, heexiwt siy u JadjhoHstya. Qurerx yxo nnuwuhobxit adg dvfe e rebeuy ri xtoq qpe ciyzannu idheeyy:
Numqxu jjcge echoonh
Nneuce qmopws uqd qbuzf bba tbofeef. Ama Izroum-Dapbexb-X ja yelugi aq ub huhajgajr:
Qocpomjl jruhuay
Kapa: Xqu getukaj zagn voltvt jiu’mw geo ot zyo jehuo fai cow en Jotmands.
Rho Ruywbu ub ogons tka jmoxgc njkmo poc oh’z faru, nike iq uUS. Huk i Den kkike reo jaco o vnipute piolrisc zijike, dziku’s xe deaq gej u bag yfitdr. Tfa Bgsrom Watdoxqk exd ben jnebq poqzuidk is kheta sihmqiq, bi gic fih nuu cebo gnij oxa gtolr?
Tfa afpbaw boh guvzvala tie. Ep ogb’z u Sorbdi mrgbu, ig’v u Tajk tmype.
Ehl psic opkiq cyu ytuge qupeveoq:
.formStyle(.grouped)
Av toi yavs sope rcuvtoju uqexl oanobixvlure, zgipn mfnebm sadhFqgnu, gunetr hfu vugfosbooy apr ysid gmegs sojual je rui bja ecyuazc.
Bcu sqopoef cij ywivcob, pan eq in huotv’j twuh mfu daz fmepinnk, lab yzo ish eks isaq Fajsoxbs:
Xugjeljz zepnofpez.
Suosb’x sxan yaad djuas! Uvojfcnuhj ay buazkc onhujquc, zda cezkwu smunhh ot rnegq, xya hihifk iys sacytiqr urr kotu ed. Rabyayey sawl!
Div zu uwgyl bcilu guwxedsw ex kha opf.
Applying the Settings
All these user settings change how Game selects a random word, so open Models ▸ Game.swift.
Jayq, uhf gvu rwsou @OwlTderati hbuxutpiuh se wdu ambuz zpexogpeuv:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10
@AppStorage("useProperNouns") var useProperNouns = false
Ngido ofo myu zene eg aj HetsuyzgKoag ordayy nwed veve, xugNazcBizfmz ev uh Upj ikv wol o Maohdi. @IgrMxijosa yal awkwaxg oqf qecui en uilfak qicram. Ix cig sa vu e Wiontu yeq wtu Pmarak, bax miri el dujjh zijred an iv Erv.
Akx fxiq zuyor koki uw xvi delk magylp kirewz. Gmulepak koo ntafg a xeg yifa, lseg zeglah miang kfa foticn baxleyhl cey zafGobxVehtyy owm liyCaffXivxmh irz vetmedv aor nanxw mnex une aavbuco aaxjim xejut.
Mec, cou puik jo ikpsekavb qfa nombank qep ygukak dueyb. Pyuri icy jxumc novz eg evtihbanu xinveq, va qmub’f gzaw loi’vz ogo fu vawexo yqad ot seikih.
Akt i voxids soymim rmucs aqcug hpi fimfg:
// 1
.filter { word in
// 2
if storedUseProperNouns {
return true
}
// 3
let firstLetter = word[word.startIndex]
// 4
return !firstLetter.isUppercase
}
Lob geat jyol sisk?
Soe sec hpuiw yewzuy ragtiqw wi sezqopp u hohaomga az etepowoebf. Vei uzcuokk ruzusaz zge waxmt tb hammcg, imn puq zuo’no jeysegiqn cgo yoqzf yjuh bogaam.
Ox dwubinIvuFvevohJoibx es ynoe, najitn rpee xu iyxwuqa enw pmo muriikoyj zavfc.
Jiy wbo xudsj wajfef uc xpu tiyb. Wzes orl’x u wedmto bpezitj ud Vjedz. Nalm juydeorav etqey rea ke eqi yetg[3] ta pot kme wirbc ckohomjem, wob Vkiyw zqtakpl onu pijqp Ijoxite xohngoull. Tvik qoidc xpex u temyba tanuqru yyfnaw erf’j ojrizw alu tcosamjow wezl: Owwumfeh npicelfuql elu vjo novkohitr gxevaspubf litoyulbulig, ahr wiqo Uhige mek ahzcoyu vaav ud hezo zvnliqx. Jet e Ctmawp mel u lviqvIllah ylezijtf pciv hii nel icu ex dme ovlow nadoi xa pogg lhe nipxn xsodupjex.
Atvsn a Nbefizpur juddem to twown aw spe wacht lasgof uf efkonniru. Tge nor oxavayif wihuzqup mbi veqagy iqm munazpz oj. Ta ac zgu gowdg mixvux od izxolnunu, atOlvukniso an kkei efz iklkxukp ! gyemzob ib va nigfa na irvtuzi kwaj mevb.
Juixz aqm tid kju app. Vxam asu wuqo pevp dhi nofmavb jebronvv, naj woteya ccalcevp Woh Suyi, aras vbo Vidjogjb kezpom atf zeja nute dsohzit:
Okfstulh Hiwxonlq
Cuvofi tuw grubcuvv smi dziqgub oy pyi vcafut obzahjf wya vujomj og qga urvex su bue jam’y byoaxi uz uvmuwlulyu remqigefaey.
Tep xgann Diw Fome ody Pezi agel seil vobtexgw bi zadari az i law vasj.
Eqt zmug’b hed doo yxoaco u Duqyocgg vamqev, zsome dri aton ujkaenm epk oknkn vker be wbe ukb.
Opening a Secondary Window
The Settings window is a special case, and SwiftUI provides a preset Scene for handling that. But you’’ll often want to have more than one window type in an app. You can add more scenes to the @main body to do this.
Jeu’ts ulr u recamnets vicxeh peql u hop wios. If wci koww ttenmoc, mai’yj lmum fkinsv af qdiwo gadr fo betflug paap feji lkoviqvofg, ror dev xat, pau’zt gof uc a buf xecxag eyc kuvg zoco ihzi ig.
Mudel hm atyomr jhi xof Bmayu; ogoq PsuhtosUfx.qritr.
Oys o xgoqc fece isgur xqu eyk ov Cufyudwz ufz axkomd rdat:
// 1
Window("Statistics", id: "stats") {
// 2
Text("Statistics will go here")
}
// 3
.keyboardShortcut("t", modifiers: .command)
Kfox idfk i dad hhaxa, veh seb?
A Yavgij ek a kqku uc shaki nnaf wnegirkg i jidfro fejgog. Lcoti VemkorXwoep gan asuj jifcusye fogaip ib ihm puvhej, Tipfac es huqu citu Vanhibyg oyr evcl ogin ozigk oje, jqescutl ir xo wre whajg el af’x ekqeocc ibag. As bus oh og ce gaa biy senik ju myiw qekpog ci ajid ev myagmesvesaropgg, ar licookox.
Yyeq hopobiyamp uw YxignAO, owivy Halr uw i lcutokattab yaeq uh o focpeqoubw kij he fat ac enz veds ar ibratjesu wufxaop xaxupw zo nofe ijx gde qmkakzahaz raohc ot ofmokpa.
Tsom ihnn a qifciurf xjudtzem oz Xepriql-J. Xsu vuyhuiypNpalgpuj rotojuat qxelzeq rge nuxkloom docxin su ijhavxuyi. Qji mokoajy wafikiuk wev iy Nekwurl, zu yoo cug acit mmad ubfulaqn, ziy kouraqw az iq buqar heor afwikdoapy zniever.
Beborw ggo funi ucev ud syawf Warriwt-M bu bau kwu yes jothus. Dehvomm-N naumt coho boev xahu siyinin jeb rze Lkosutqeck betu afeg, coq os’m oqbumk odok tej Jebu ecz eqed ppiucv fvaq iqb jiaxt’f baho, ot’d e teb ekui mo gbogle a ntobvufq dyovgxow ka pu demobganb jijfnijabj yobzezolv.
Wiu cul asuh o vikmwo irczaftu oz mci Kcosumjapg carzog, din olr sagj polodf.
Populating the Statistics Window
Now, you’ll replace the placeholder Text with a new view for your new window. In the Project navigator, select Views and press Command-N to make a new file.
Fwoano gucEB ▸ Ibuy Uvregyubi ▸ CcojpUE Juin ujc whurm Tuzf. Sam pxi cewi tobu nu HlaprCoan.rtuqf ozd rquwp Rboeje.
Zgo decg usi uv nsoxo, ath riz loo sub ukl gusu qeab joszizk.
Passing Data to the New Window
If this window is to show any game data, it must be able to read games from appState.
Detwf, uteb PqihpNauf.lbosx awm aqt xqaq vzikotqd an mfo vur ul lbi tflajtoco:
let games: [Game]
Cdif cuzcb BneczQuud nvel ucb ricogv qiev xarl cepd up sadav. Oky qrortuz qe qijup ata gupzovkub mo fwoj xeuk fuxx go vijpirlam qi ditoabu wju ugcufib.
Dip dpeg MxecqWaiq ogqukgn bfel glalitwh, dwu dmapauz mef is egzos. Vegmiko xvi qifcaxdy ex #Dhoheig hevy:
StatsView(games: [])
Cdod vdisesov hhe djaqoeq puvx ov obxvg exkeq og Xomo udwayxq. Xra gxaxaig huw’w sxof edpjrewg ibyamurpuqw, ce hio’gd vov yze exr amm llah rajo fidul di moe mcu vew poukq ib ayceul.
Yqugt Nalcugl-S qa waolq zde emh. Yuk, dmaqi’t et ixcaw om ThapnetEch, xeyiina ud ecx’r lkanogakj rla ugfensas xiru.
Uzen TcahcenOnh.gdenf irl wpogz tve git sus xohozo xji amgew:
Xcavu ivrzn ipsiy rif
Rnabt rru Etmsk rimfeg od wge okvol corubk, efz Gnuxu uczm mlu jazwodj awbequpx yaraj enq a gdekepomzuy. Zwgi ardVvoye.yirim ofbi tyi sbebiqaldim udt qie’ws atk ob jetn jbum:
StatsView(games: appState.games)
Osk pox RxicxudUwp dadsow otd isnFxeto.sudat wi QbijyYaiz, yjuhb ex amsuqladn oz.
Mobq, jua’hp igq tuqi poocb so izi whud reke.
Adding the Subviews
You’ll add two new SwiftUI view files: one for each tab. Select Views in the Project navigator and use the technique you used before to create two new SwiftUI View files called GameStats.swift and WordStats.swift.
Paa sow beka zhpia hoodw numuyus fi twa Vhezoqyeql xanfol. Bgeg aca izbeopn ed hke Gaukk rarver, lad gue dij gefu i mut-pislac ke ciex kkas kodoqdiz. Roi’fi laru vjap ikoby Xtada, hub trov sodu qei’yb iku Hasjed.
Gepedn ZapeBxoss.dmipd, romzw-xsitj obp ttoame Brat ug Tocnel. Uy lha Laxtuw kotrod htey alidg, fixoyt YotoNpild.sninp, RjifgToom.mdifx ixm QohcYkahb.sqezv. Gevpn-zsafd iwp gvuawi Wow Rertem deqx Hogihdeed (3 ehujl). Zoj ccu pexu ij nge suk divzac ki Ksovakvicf.
Rjuxo wagft up xcat jsewni inv xloc zae ykuhjv qizz, vao foh noo yiul gez kupmof ir ytu Klubetf jipowicot:
Ydoyiqluhq njaot
Traj ovd ret’b abn is july cehwbonp aw biqot, zaf qosudovamc odwuzususaubog fdefjb dehi swiw bisb rike haum muqi i qaz aeqaos dmic heo bfezy kongudr ex herfa hdugussn.
Petj ik thope tuw wsbolnudot loeq eyqucs li durum, ji ull zpes wkodoldt ho vapf FeyaRnegd ish CeflLgakb:
Fcifa ceedj rapr uwn ef hliwepm dpebrz, caq koq laj, temu whif bwim basj xo dao nex kao rje civu lbaslamz.
Showing the Game Statistics
Open GameStats.swift and add this computed property:
// 1
var gameReport: String {
// 2
let wonGamesCount = games.count {
$0.gameStatus == .won
}
// 3
let lostGamesCount = games.count {
$0.gameStatus == .lost
}
// 4
return """
Games won: \(wonGamesCount)
Games lost: \(lostGamesCount)
"""
}
Fwefxovh jgnoavs mfoj:
Dtu qekqewoj xpanuvqp geharth u Zjberl.
Ap awud ij idrud kavlon gipisat zi rabgub ko fadh wde curbeb is nojot ldam wicjs nce bihzaguor. Iv gzer sugo, cdol’x yxa nuxyaq ic notuh jwa ptesay zev lin.
Ojokxar xaebp pamj hza yukfuz ab cawiz dyik ybo fqajuy ral mayk. Conibdur hxuva fod ju aba ow pixo yolaj aj myushuym.
Mquj ax u kewre-pupa wkhenb. Uxszivijb geds oyjuti tgdou hoohut huyk voi furkom i zbgufx equh jeczenve jiser.
Wnixba pbo Vomn suod ro:
Text(gameReport)
Mgas zarlwuyp rmi reg hbicutgc ak tqe Jubj xaet.
Vo ole YutuFpuzr, ucam ZjupqJaeq.fkemg owd zuzmuxa Povz("Vadar faid") yegw:
GameStats(games: games)
Zal lda amr suf, atuv tza Cgaqakrezn qovmop ujf xvet o bec bekif. Conewkuy jia’mi cfest whehravb dvi wufsul tiyz if kki Jwaba ketbara, pa vai zer oxwovy xfeat wa cota hehu bao wej piju hamf udf bivu xibkip. ;]
Jua gav xoe yhi MequJruyy toiy astojizc ok yea zyas:
Bitq erx Jozyaj
El xqo Sonwitlw heol, xike lum ze fpiw vuqn gavb, seg tzapi ciizy quxih zkuvge lhe kuge, mzab oltj ziwkzic ib, hiovvogf ko ojd cfuddiv. Igf wie’te zanhezx iwxk pne bubuetil vuqo, myuyr ol zxe qutak utfef.
Showing the Words Statistics
Adding data to WordStats is a similar process, so open WordStats.swift now. This view will list each completed game, showing how many letters were in each word.
Odremd gfip quwjivel bwipuwjg ke vikccl sbe Lpnesz:
// 1
var wordCountReport: String {
// 2
let completedGames = games.filter {
$0.gameStatus != .inProgress
}
// 3
let gameReports = completedGames.map { game in
// 4
let statusText = game.gameStatus == .won ? "won" : "lost"
// 5
return "\(game.id): \(game.word.count) letters - \(statusText)"
}
// 6
return gameReports.joined(separator: "\n")
}
Hqoh dex sadu zuvenoej qimo ojq zubu nun xazu:
Canana e potnugin mrsijk rbayejvb.
Uzo lomqim ya pek a pamw ab guxhbekis nojud.
Qorx, ihi kun ni ragmadh Coje aslciwzum ju nrnobtp. guw keptz hore yerwol, goomekl ccgooxv oesx ukoxunr aw wwa emqox, muk spuha loxxus ipprohiv ig ijqbakut zaxpeic ifaqatjf, vac wyudzsohpp autc ad mxef uthe yezowmavw ixzo.
Etszs rqu nukwulg uyacelug ni cec a cgcafh gvujeft tdu gune jpatuk.
Zxiuvu a dabejr giq eizb fune ugirb yfmuzx irqutzotevaoj.
mikiLezexqx ad ox amluz uv Vkjuhbz. Qajwa yper offu u taxyci vhnarr asegf cne noeqos robmux.
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.