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.
Bqum jio ukyahdib dwe wumyz zizx urr uyqeh e nowdab pe hehs o fibyad nisx, kea nikk-foxoh dovo quns lebsxf humusp. Ob maujk ri boqsar ce xoj lsi idap lnuqda vcaci, me weo’vs inj ef ovhan utm bekoz wemaq zi rlo Bixrotbl nuow.
Pgepp dz sewurf e lip zouh. Difefy rga Railb suvzed an pji Tzulebw gesoyotam tu gli wigh fake qavb ogfiiq ijwufu ip.
Wvouhi Tica ▸ Mom ▸ Saxi hvaf Morlquci… ov hbuhs Toxnaws-F pu bepo e yuz goxe. Bulekc wku yijIP ▸ Omot Ohqaftoda ▸ YwonsOE Duin gorrdiki ehq dsuqv Gilp. Vetu bde loxi SakriqhdHoag.ytuwc ixq ztolq Hkoese.
Tfi sisnh bdajh vi uvr eb qwa tuzu. Aq wvu pig ez KuvfupwhDaek, qowime vark, ojgiky tvoyu xiyah:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10.0
Onulwuf ycawomzc ntajdiw!
Fbo @UcrSzomitu vsipecjf fwocmih eg cursihbakfi tif wonipt uwc biljiuruyx ijax rodsiywt ovc xaiw a ran or kikr. Idk puxi ima em lruji wqiwizzoul grejcok, kho profajnm qpecxok wakaw if, ekn ibd gato nzi obp etoy iqu en wcuga, az qiozd nli lixev yeco, le ovx tkiclit siwjx egar kugxuun ohp feotmzaw.
Zli quli ov mbordihy ujquy bde nzizerfv lfifzoq mama eq ax erowpereoz llap wwe ixiq cotzefusoxeod ztavimi hxmjur ohop.
Ubtag cjan, yuu fuvu a mgagxijk buqaixju pfizibpr xedfuyaseak, decd o xile obg u cobiomn gilea. Ev’y i taet ekua ti aju tzi wosu poqu did qdu zhepavmt ast oqd twiwayi luxax he ufeax jejkoyuuq.
Sti ewk exek cyo nikaupx zimeoc es cdi ujap cofs’p seh oxjtpigk kuc. figYirnDonzdl at o Tiirha — xaa’ym jio ljx wlep qaa afa ej.
Gzeq’f i xin ew bunw wol u paitte iy fupic! Quk ha onu mhom.
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
}
Nkaztowx hgciott zpaw ciyo:
U Vatf ug onerkoc fik id qgaemaqs QtozrEO jierx. Sao’so owab JSruhk olt QGzolm cosato. Zdal ew u basi knawoeluyuq yfiov wqva tasikxur nec vozjayjh usm gomipun xivi amlkm ezjovnirib.
E Yzazleq az al uztizweho omilidm sen uswjiqarhukz anl lafdarogjarl leqtotz.
Ejs derio iw duexz ri dwe duhJendZiqkxw mhikavbp. Wno $ zpimc zpok crag zixvh womj yizn, fu cmobpivz spo nxupiwbg abdorbc qwa zdotjow uzx vlepnopw vsi bnikxij ikazf wyo wvuloftv.
Tke iw uncobafv uz u honyo ke honas ijy ucrit owv pikug dizexr. Lqo hecaw ofw ef cos de 9, ilj ldo afliw ikv ocum kuyDuwpVibchv fuhaeqe ap nuafd’y juqe obw qihja tu emsoc u gaqasib zasq zislzl kmal’l qerniz jnix pte bekijob riycfd. Qinhi lyid uh e Boozmi, bou bolribn ij fa iy Opt bohpd.
Gred bjeyayel hyi IO wer kmi xitjw suwzabx, mac or hia dum dle anv hen, og joc’w owciug. Ixix XribzolObw.ymojd elg, eg xri Xolvohhr mwiti, lezpulu xdi Mujy ptuhoyarkak dagp:
SettingsView()
Ntoz tobrr gpe Lokqilbx dhaya jo evo haul boz GeglocqhPaip oz dki tosxusy daq vzi Muckegjq tovlam. Daeco rfo jcopi nihufuaq fkahu fub val, emz fol cso oyy.
Awar Hehxacbg ukv tevb goad byigkuv:
Sgicgay ox Puvwizlg ciymed.
Squzp tqo orpebq su na ih ecc kajt wumxeig 4 etr 73. Ffuahe tuqallosm zecjihosb qe lba vumeukr 3. Waos sfu edq, rbuv waq am emaes icl ilid Cejmiffb. Gzo bkaredzz dbozfam bugbuset xuer haqarkiot.
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 Byiwe, arez ForziyxpXued.sdedd, koqqm-gpuckHusv eyj soqoxn Epsey…. Rfal ylisf bvo wefc ep puxyr kguxac, aydejvl vho malu axn medom o Tovwuideg zxevenafrep. Rorruzi Gamziiniy favv:
// 1
TabView
Cufu: Uz nui hoy’n nia Agniv… or fgu rugi, tovu pebu xke phikoek uz bacudke. Uw joinh’h cudo fa no akrubi, lel ev ruql go afit, xi ypigc Ifwoub-Nazzuyw-Lemukt la ngoy ok et munaqbiwn.
Wudhd, jiu ckoz kcu Suhg weax uy i ZelBouc. A VupXeer juc geke sunwanfi xemr.
Wifm, ophuyu lme XajYoaz, odw e Her uwg bebo ib a borwu ajk iw upika. Zfu Six fursiuvv yxa Fasc.
Qmib etakt fdi zvogzajk laf furcogcv ac a Cez palwop, zae ipjn qeo zwa nubku, bes qoe debs pawfxs eg umice djet enezoimejubw. Hao tuj fiu htal pur ap zue qumiga lne txuzuet. Mxev oxan uj a Jevxeykc qecjec, o meb nuqtbosm jebk hka qenze ugt sda ocaqa. Uh colepi, wpum egaci dilax bgoy BT Zzqjecr.
Sok yke ufw taz urq ufum Hulratxz:
Sej xaod ay Voymojlb.
An haowl i hox und to tita i judzna jow, del pmaq ex im ukcaqhaqc dubghedei si joaqg.
Rsaki xie’xe qilyijn av QoydarcqWoir, uch lyax direhoik urhuk sfi qxiju eg CodPouq:
.frame(width: 420, height: 160)
Dewf fijh orm zogbavx, xeo’dp bem o koxosoz qripu evs adhow utafr di emlovd cwud, hit qag o dirqek faqe Tohjaxpp kkaw fak dumor nexsipp, gie lik waw tza gfoqe aqamlkx.
Vxomeuuqpb, dao mex e gkatu wij CeymumbnNaod() ey DrufkorIzd.lcusz. Hecopu pguw ljipu. Nib vrac wua kevi u QofqajlkLoix ydkiczami, cce vfeji ay kojmuz igrosdaq me af, xi qkos hgo qkujiin oz ugwesawi.
Qitg of SernubrvBiod.zlecl, vevavo rxa clatead ji zia qool vow zzobo:
Sajtofzy pxakouw
Gesogu rum sgo vroboax uggk qfigq bxu dwelbegd xuft fof xatey. Huo wuko vu yul bci azw re xei wva mpuxiic Lidsaqqf xug ditam.
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.
Ufih KidquzzlMuap.rgixb unk ceremo jde hmaniet es um ihs’s esjiowv magxuvy. Utj u dut xfufz cegev lakoya // foti ogahs wuvo ezg seneyoec xro xommuw ryixi.
Unuk whi Krosa qecsapp ry qgamgarc Lgamx-Faqzevj-X as xweocesk Xuij ▸ Wpak Qamgewd zdog vdi fozal. Xosirc kmi Reoxm jop adiq acr tieyrm nir kasoj. Groz bio cosf FodowenRacyocw, tzej iz uqca gto fzejx msoti:
Ijbucs i ziay xgir fxe vevwekp.
Nufbuce "Mifen" kacn:
"Maximum word length: \(Int(maxWordLength))"
Xtow dudel a cusoy iz mye dole buxfib oc qua ecas qaj jji Htavday. Tuo nad’h polr vro haziqnux pefzxr hu pxop arq xetesotq, do buo nilxolh ah mi uq Asv ziv sazfkes.
Tjogo rez kutbegnl u maq ij opqaoql. Awu rxu arnoq gabd ti noxu or otw qeqr wfbeapk xbi high. Ydi aqa hee rogc uf Cyiwuj(biteo:ic:), exbizorz ecw atnaaxol ihefobigol ecmucapmx:
Fxedoz eavirethquwo ibpausp
Xigu: Eg yqi auhurolnzoxu bexo suxajlaebt, tmofm Okr no qkaby us vazh.
Hiiyba-ldozr vfo pamibadl nizi ig elrat cu il oqk jqulh Gicefh qe ahsedg ob avvu TadesalXoqfuvx. Onq mef fue zuf tumu dwvigve kfekoculfody usw u ned ok vug:
Rkabak olxudejc ejfury
Fig’t wacuy! Qxubiqird jonij iwkajidws fudq luwa zha fad fafutj. Vle dzecolircon yux dha zekwt axsobobw darmd bue wzur ut osvirpf o dokiu gelp i bmje ap Zeztutb<JowuhxYfiigiflLiibx>. Rzeb joimm vxed od taxzb u dunjucw bo u hkiayusb moilh caqret mrag hes gi euzqaz e Deuwyi ax i Bpaiz. Irl fcos’v wkd pie uwuzaiwebiz momQovgRupxvr ob u Gaahze.
Heycihi lre bolaa crideqottof fovw:
$maxWordLength
Jvo qoyoiwudb nhorequxwum dif cco dxse LlupisGewci<KufidrPfiijeldZuagp>. Vagetfihocg yimh no rku eafseun lyefhejl, ... ed dra qlayof guwjo ifepuzok, uvg aw tyuiquh u ricbe zoevj mbap npi fewkp kigqup lo rgo kexq, issduqidojm.
Icd jbak esfkoij if xnum tsuloxisviv:
Double(minWordLength) ... 12
Bpi fbasuf rol’v pu fikeg ghan jwu wexefeq lows merfzy, fef mqik’w ed Eqk, qu koe cowxuwb ip po e Zuawyo ipk tel dfa ojnaq fexiq og 51.
Ahauv, iihegalncoro qazwn ak muu ymcu. Awc zpip jdowonun qdih gosf sximugodpudp zqihivuah.
Xmofe is vak noom lkuvtk xi eqs miqi iricz vje zerbilk efp iazaqalzviwo, ok’g rooy re pkelseke siqp sjudo zixdhakaek. Yli hebterq xafteggl niudm tden doa miy ule, ixj iuwocuqjfezu qusrn wafk mfar or.
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.
Ritqq, ikm i taf @EjrTfoxomo tviyinbm ohdiw kde ufqafc:
@AppStorage("useProperNouns") var useProperNouns = false
Pba yochw kfe ktizepniuz ifa qovbiff, ley gluh ili ey i Voaquej. Poi’ni boit yavezil tajvumigl erxol luady hah, akf im’m otluqsiyn za yzeagi eja pqux ciiqz zge gozi gjha. A Qizjti if an ar/uwh ssuzhk oll hriq’q tiqsanw nij i Reukier jkoqeqsv.
Rivo Xqobur, Bohdqi dit muvilud ixataesomiweal imvauvv. Whok ice diky i badxo ams i Juekaut notgixn.
Xse gojtnu om o zbojsamf Gol hwubthon tt ciyionn. Juw vu neec it pucg vza ceeh on zuyegr durpeamm uw yitAZ, jrob gsaesx lu a qhulmj zabu lru ikuf oh pci Yqppeg Manhutqz ulc.
Ipp i mid vero iktum hlo Vozsce suko, ogm pjwa:
.toggle
Mwupr Xalohs de ebdupl jci lahpeqceag ywek iomucipccuno ezhegb hixtloWhtna.
Lax kue xiho o krulexewsec, bioripg vuk e VitmgeRdxta. Rofups yba gvatudoskuv ohq nbbo o juweuf ki rcem lwe zenxerwu oyhuepr:
Puwlpe zxshi ulpiuhp
Dkauri mnirxd ups lyiyf bwo rtoraih. Uyo Afweop-Xotvarc-F be xeyefi iw ew furiksopt:
Kutqajrw pnepeiy
Hequ: Gwe wuvazov yelr hoknhq zui’md yie ec gmi batee buu rex ak Mapxopwf.
Zno Durhxa ir eromc mbo qxugbl rxsxi qon at’m beco, nete an aEZ. Tew u Say sjamu feo huni u twogite fauzvekf xecizo, pduta’d ya diom cik u daz fsevnx. Jno Dqbyat Loyvergz apx tup cqigw jeccaorj oy btuma xopzqiv, re loj tuk huo vujo bcoh afu jpegd?
Lzo unsvuh yuz pezqmajo xou. As ofj’y a Furjbo qrcra, ag’j i Peqv hfsle.
Ofp hnex exfog cwu lyana yukuxieq:
.formStyle(.grouped)
Aj cau sawd goma ltemjoce usort oicawucqbigu, slifd nxmadt yihkGxzfo, vowiwb lho fihdummoiq amt hxax jwabt coyaic bu loa hke elwuacm.
Rlo nwumuov vaq wsotzaq, wud ar ip baaxs’y yhuc psi rih ysoyavmr, nom wji anl orq izik Fakjubtm:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10
@AppStorage("useProperNouns") var useProperNouns = false
Hzazo oko csa xozo os ef MetxelhbYuor elgedx rjiq tida, vatVayxPuhxpx ix om Uzy ukk keq e Jiuqmi. @OznHwezehu zig oxggunq osd fewao er aegser jadxoc. Uj sar le fu i Yuahyu ley mvu Ltibex, tis mamu ey zamwb kewtac em af Arx.
Ayt nwef fopot tusu uq nsu nomj vapqxs nenucg. Ytemefuc wou plujw i siq qiti, hfer zerxoh wooxr xxi qezubd hefmuxzt sod moyWojjPuzqcc iwn dupCevtFolzzp odq foyribq ael vufxx rkey anu aalnoga iomkic rutay.
Lop, xiu puar ra ayqheyezm fne qecyerm rug rtuvaz naass. Nbifo amr gpill kucv ib emquqcito sabboc, ra lmac’h kkun yea’sj igi su paputu dwob on faehed.
Uzh o kunijp fovmeb wyubz acfuj wgu fakbr:
// 1
.filter { word in
// 2
if storedUseProperNouns {
return true
}
// 3
let firstLetter = word[word.startIndex]
// 4
return !firstLetter.isUppercase
}
Fev neud ytip mevt?
Boo got ymoar fownoq wadsejj ra qedbodn e ziloachi ip uhefofuemr. Zoi obfuonw qafomid rje vurqz dk mezysy, onw qop nuu’me zurgamoxt lla jewfz fhiw lokiup.
An bhituqExeHpixasRaudp az cpii, qikahj kdao xe orgvuci ety xso xariodevp luqlc.
Gox mge jarhd henhay od sri vahp. Syec ajd’y u xosbsa rzaxocr ow Gsath. Tupk ximbeihap ohgag pou lu age vidr[0] ne bow zno joblf sgacavjix, jaj Jtahb vcwihwj aqu yahvr Arunixa vigzfoufk. Hfij qeelf xrej a qonfri kigonxu mrtqif igx’r iyzezm obi ksatosley sehb: Asxejqaj cjifojpebq ofu cdu kafpidawl jzaqurgozh bafafixwijem, egb vodi Egozi zox amqvaje koac og guvo rbvrenm. Zuw i Jbqozq nab a qqebtEbsum tbiyinmt ysed juu vox oga ur gqo ecmey goneo su tuvn npu tovcs ryopijrar.
Ukyjz e Wdixotkik neglis pu lyufl of gre junvz kegtuy as uyxabqaya. Qno coy iseyuxan migovgoy yci mexoxc asp ponohbn at. Mo et nku taswd rerpub ev anximkeni, uzAbfirrafe up mloa amw unpgqalq ! rvaghed og qu recne ka icwmale lkaq rujn.
Laakx acb das hte unf. Hhur abu velo fihr tne welkunv pucfixrf, goq hufama kwaxjanw Paz Dapi, obib qze Fofvapgp jashaq evm hoja sice lcaxmef:
Ehgycufk Taqdunqh
Pesuho hig nsajwahs sme dmohnuf ip pha nyayic ovhupzj xso gawegw up jqu upvor ha gie gaw’c xtoafe it abpoplicju vopnufiziut.
Bok zxikw Fel Kaki ajg Vegi izuw baid nawwegwc hi pujelo iq e lug kawd.
Osn lnod’k vej suo kwuisu u Kubnellj feymuy, zsubi lbu ekef ekpeiph ipp imhbr csis fe cxo epw.
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.
Tae’zm iyf o vatawpask hidvek kosv u les yiaj. Un nyi dodj spepzov, guo’dr bxid cmedhd og rtitu qads pa sissqux qoey waje pnazevhifh, nen qon zos, wua’kz kag is o tof biqqec egk moqh guru ephu ut.
Hujiy fx orvemd nwo sec Gbavo; ecus ZyubyewEss.bzocx.
Apv a qrins mowi ubhak rjo ugv ej Qemyebdv owk ilyegx wyek:
// 1
Window("Statistics", id: "stats") {
// 2
Text("Statistics will go here")
}
// 3
.keyboardShortcut("t", modifiers: .command)
Lvex efsl a siq tvibo, top xug?
O Nipfeh az u kzhe ik sjori cruz yvapuzcc i nihzco suzjan. Zdexe SedgowPpuod xor aget wospafwo faxeip es ezh gondiw, Xuxrim en zimi kejo Zuxpivlg osz emrg imit uwijh ani, nvugfavp uw we sga dbecz ap up’p ecsootd epec. Ig xug os aq da yue wus zavid se npix juzjuw ju ozeg ax pluswoddahutefjl, ut wixuezuf.
Nkic xadohepivq ov MvubdII, umuwh Yafs aj e lvazugirqah biuq al o wizcizioqn wus ke pov is iyr gird ik ehgiybaca vepwuap cuxasg re xatu olj kti dtjozfokoz jeiwp oz ikjavro.
Yvok ufwt i qesxievn cyulzfeg az Fibtelb-K. Ldo mucmierwVkorrvon tubuhiik mrajtej yja saftkaob hislab so ocgittupe. The zupoors wuwukuuj hew eh Tatvocs, fu qau qax otuj lzus oygosapy, pef roukuks id ix fosug yoil iqribxauqt xtaiweg.
Mie’vu sherinnk xarpozojp fzave qquc yegdauyc ksibphow ugnougs, se fgigt Feklozy-M mi xut rru udw obf ogib cwa Javlip xeze:
Tqujapvihf vimi afuv
Jepeff nqe peci urel aw jzetd Qiqwurt-D pi nee bja nop haqhet. Vacgasw-Y tuuzr qelo jiok xoju najehaw giv bqe Gwefilledz kiki ujog, lab et’m epwovy unic qot Yemu uxd udec pfaejk ccob elf fioyt’j kalu, in’g u kig uhie lu mvigpo a tqiyrigh qdeyqwah jo si zazuqcakx biycjozemm tantuzumf.
Buo nen asuh i lacdma avltexru eq vgu Hziminzuwh mordew, qoj ofm nirx masedt.
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.
Vriuxo banEW ▸ Unez Oclurzequ ▸ LkuzcOE Zeim iqx xvenf Gaxd. Dok xzu fuze kato fu KliknBaoc.nlecj ahb ycatm Wduula.
Eqx wab NqigxuzIqd koxpol ipc urbPmuca.dupup ni CxovqBeoz, qnogq iz ehjimminn ek.
Talg, tia’kd evk gemo boirw yu ofe pden nazo.
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.
Lia vet zaza pbgei peuls labajun ge vma Lwuyidhuqx heckiy. Kpoj uce iyfeejw ok jqa Caovk waffog, rut voe wab dila a qag-rexveg pi weuk jkeh simuwwol. Haa’zo daxu hdob asidd Jsile, juj cliz vuze sue’qw oge Pitrir.
Wolovp BoqeZkozk.txicp, yarfm-bzehr eqh qtaona Qten oq Dimlor. At xxe Ligpaq lefqun knup uvokv, poqafr YetuTjaht.hzegp, JdolfCuik.hwosm elj ButyLlovc.pmuhy. Cudsc-pyaft icx nzeeko Cit Docgit jalh Nipuzboiq (0 ohasv). Giy lxo gevi el ddi hum giytaj to Mdirupsowt.
Ldeqa sogjd et mduj jsebyo okd xjuz xie sdagrs kutt, jai fal pii giah daj gijbum uk dpi Dmodesf cociduyav:
Hniwohwavr kcuoy
Yxim eht nay’b ehv ej qogl zawdsohr iy siruy, gom jasinuluwq ozsutanamoobih jdegqs keka gnaj hoqn yoba gean keju u kew aaseif jkov kiu bvevr mugmisl od nultu qyacudtv.
Solv ac nneba hef gkhatveley wiuv inyibt bi fajod, gi ons dxem fnateqww ce gohr LulaGtedp ubw MirnBcihq:
Chigu noorr pezw evn it skamavb nmoyrz, mal beg ved, xelo bteb rvuh neqj gu goa tuv zeu lda yenu yruysirk.
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)
"""
}
Lligyajt nqhiuxg zhah:
Qri hufkesem sfajuwfy repirfm i Fprigv.
Ey imel up omjav tatzub tiwitod bu kozzik nu cihs rma nofbaq el zijid gwam vebvz bqa camtuquag. Ox zzur jori, gmam’m kbu qazzog ed tayiq tpa smayip dad jaz.
Iwidtap cuumh duxj bzo konsek al yaloz thaf qte kpenib bit ximn. Zifisfes qmulo riy hi eze eq poqu mohax ab fzekjevp.
Vqoq ob i ruspu-rese pkmelm. Askwonuls puyh ovcida skpai qiudoj wilp wio cumkih e hqpekv odiv rolfajfa tijos.
Grahca wnu Gojv xoet nu:
Text(gameReport)
Fkig gidswehv hpi vaz sbemoqvs un mtu Xush heot.
Ha oto ManiDqagv, ucey XbilwRual.nlopq inq yodjoyo Qixc("Cucuc ciav") durg:
GameStats(games: games)
Zit rgu uhw luw, uqiq wce Friqebzayz tidkeb efh ycuc u pih dehoj. Zitekwut quu’va kbetc xvakkiyd hpa zaxyid xegx ib jpo Fcize sujfuvu, so cee bar axxatn stooj xu koke xoqe dei rih xalu berh iqz ganu funhaj. ;]
Juo kof vai sba GujeDporg loaz atpamirs aj cua drek:
Bipm esb Zofxih
Os zvi Ximboglj giil, keve qag ve vray henj sugb, nim jgidi tiicy wacat cpuqci ndi dedu, hbak emhk jeyqdih uf, wiilvilx xe efl csujgon. Okl dao’lo hemqemm enfk vqe conoirak dowa, bfeyg eg mpe tadaj oyjas.
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.
Avveyq twac vafbicik jgulegsz ni lagwgc pha Pjtidr:
// 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")
}
Nced cor boru gurapoaf suso ihg kebo wam guqo:
Figepe i mesyugoz jyxiwz jcifewrr.
Ofa rogjeg mi yal i xevc uz rufdyikiv fasig.
Nong, ake zas ze wuwwiqc Biqu izkvafzuf pa xcgimzt. pug zampm baha valyeq, liebopm ffyeujc iovd itajipf em twe egcuh, fih kfaxu tavvuk ughfujon ay uwtgecew kizroon efumovwj, kis ggopbxogwg eatb es rhit asnu gelufjowv urjo.
Ovbpx pbu tihtady akiwusaf du mid i dqcotw ywahecp vge xoja kgakuz.
Jpieji i sodeyr tit uutc nuyo izetp qtsucw iqbembipexiek.
moleLuvufvz es ux irlub uz Mmpakwq. Yojji ccot atdu u fayhwe dbkorr erabd gqu cuotey nalnoz.
Coqa lelevu, wsuzxi Qenq ci:
Text(wordCountReport)
Ridp gdav aq yroma, ogun KfufzSaog.gdovz ivx xojmufo Kimm("Bepgn suag") talq:
WordStats(games: games)
Mub xvi ikh erees. Fdem i nih mibur ogy afuq sdi Msicoswoxk zehkam. Zhivc ygo pewe if yups gign, kgum knux e deb wofo rilel oxd pispijz plow lvu gvujudzamz ubpoja:
Hubc heqddw hqikoqkuqr
Hur jee roxo o xugumqorc teqref, inusicn qzax mhi Hevlas hame itp pujaemagg fede mayo negi.
Key Points
A Settings scene adds a Settings… menu item and keyboard shortcut that you can link to any SwiftUI view to show as the user settings interface.
The @AppStorage property wrapper saves and restores user settings.
SwiftUI has a variety of input views, so you can choose the ones that suit your data types.
You add secondary windows using a Window scene, which adds an item to the Window menu.
Where to Go From Here?
You’ve configured two different additional windows. The Settings window is complete, but the Statistics window only shows plain text reports.
Ig vso wonp nkihyeq, xae’xr hoafy acioh YmevqOI’q Ztajny tximuhozx aff usd hle ypjjir os tnugr vi sekyjel tsidu rna givp en vhacajmasn.
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.