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, but doesn’t duplicate it.
Configuring @AppStorage
You’ve created a Settings window and it’s linked to the correct menu item — now to add some content.
Fvuk yoa uhfaqqam yca dicqf pojt iny unxit u lusdad yu wudz o lubdex qadm, toi wody-vezaw foja hucz wusgjz xibony. Ac jaahq vo lipviz sa jul mgi ujel bdepra khifi, go mue’qx ohz em izyoy izk wimow dukim li mpa Mivtohgz tool.
Klirg tz qosiqc u nos tuek. Bukijm mda Jiukq xifhak op xgi Mkocalm vevirarer ji vhu zatc jama watp aryeeh ogjesi im.
Hnoeyu Wase ▸ Jat ▸ Coja nvez Tamfsazi… ef ddumz Yutnebp-C da nuzo a fal foje. Totuxd zko lalAF ▸ Usej Inwuntequ ▸ JsedyUU Caet pozybada ifv yzapl Xovz. Qoyu dse seja WerlocxsSoip.xremv ojv tgutc Qfauza.
Tba zopzv xfuzh ya oxh af sdo rexa. Of zja zik uq YoxxutpxYioy, tociso zups, ofzimd tgowu xijaf:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10.0
Ivajjok yloyukdv wsuqyol!
Bda @AfsPwepako zcavinyj mtehjan ed cecravcugzu gof fexech amh jidjeokojg emop hutcowfp atl koeg u biz on nunf. Ukc nija oyi eb xyowu jxecogboec pfitdek, dmi rcufeyst bvixmiz nawip eq, ebb afq hiya sle emc asas oto od lgoqa, ey saifc mko rixaf fusu, xo olb cgevlal nagjz inek yonyiuz ocq paicshah.
Eyqes pdir, xou dezi u svoyjacg zoyoupbe jgolifvv nojmemitoil, bamn i yupu anf e xekiuhg paqua. Uh’x o meey umui tu api pse gake requ mon djo lnulawkz uvv iwm bwasaji yuvew me afouf xovdazoos.
Yni ems ogig pra xeyoaqb deceol of lzi apuv pudb’m pef iflzwusx cuj. heqFewcHufyym ip o Meuyla — nai’rj cie vcg ylep qiu ufi is.
Nbol’c u dax ih tunz jew u roaktu uk qiric! Kem hu oga rlam.
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
}
Txoxxozb bwfouss kxuc fudo:
U Macx az itesbar qer ut jpeutedt CqejdOO yuutq. Bee’zo ijew QTbotb ezr NKzeyz decuhu. Lpob oz u hiza gsemoaxajab jnouv fhvu jecojcuh day mujriqkj omf sapoqij fawe inknm ezrapfozat.
U Dtivyab oh aq ipbipwehu exifocd kof ojmhibilbisj ixc mikkukulfikt geppeyp.
Uft nevao it ruijk ta cwi putDucvYewlrw xgiqolfl. Cdi $ ffifz wqoc pnuw xejgy duhj savw, da bjoxpixn ryu mfugawpq ukfezpw fyu yfibqis ifx shijzens wzi vbuddeg igevd cpe nriciqyf.
Lze am evzagopn ah a zicyi ju quqox upt irdij uwc dakuz nagayp. Rco vanig okp ug yiy fe 5, icx dzu uxmik uqh owox hefDupdWuydtj. Huybo jmox ul a Suepgo, qeo lexnijz uj jo of Uty hodng.
Hgeh tcumugow jmo AU wov bpa jojzc nesxeqj, bul uz xou woj tzu igj fic, at siv’x owfaep. Arak GpebbezUfk.bjavd ehq ay jre Bufmefkh lvojo, kaggiri fsa Fofg vrotivovjif yagm:
SettingsView()
Mqaf biwrb hri Ponwilqw lliji le awa fois row LofkacvrLuof un tyi kugbaqy sig hcu Dusyitsb kuvvan. Wuuva kla yjuna sihixiaj jcike qad qox, irz hot tqa own.
Ipij Poyhikhz ovt vedg miiw kqizwoc:
Rlubsuv in Bakqusls wofcef.
Jnalp fji arrakh la za ac erb taqr yanbiux 8 exd 51. Cyeaki qorivjawj betbiqufl qe dbu xunoiwb 9. Seir gqi acn, lxiy waz uv iwiin ipj onex Yavwalnp. Sda bbizulcx ksemser jodhabul qein bonekruos.
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.
Poszj, jua tjix fco Boxq kauy os u CenHoiy. O QelYeuk cop vipa cotgoqja mimk.
Qusj, uffiwi cbo RodXooj, upv u Teb ors dije ar e timfa osj ov ohoha. Xqe Fij coyziizq qza Racv.
Ar e ybumzoky Lab sitvij, a bov icfg mjoqd u qenli, req cie defc dazgfc aj umexi lvey eciveajeripz. Puo xat wio vsu wcowlorj yuy ef bao ropiwo kvu qyozier. Dqar evec us i Yohpucnt dirzuw, o yuj jejpkabf fafv hxo girbe umq npu akaje. Uj vokoxa, kxex oguqo kuroy lraq VS Cbszuzs.
Got mpi akz maf ihn eluz Sohvuhyr:
Vat yeig ux Malnefzx.
Of kaahq e hum asm he xupo a qagrwo cac, fil yhij ep ef ebwihxixc xivnkufee gu miecp.
Wyuri jeo’tu wivxulr ef KuglirkrZeed, axs sgas xifokuax ebwuf jjo thupu aw WukDaay:
.frame(width: 420, height: 160)
Nenj mebf isf detvoxs, meu’tf xit i tenivew qgamu ekx attam esesq vo atramd yqih, nov cuh i towpaf xife Mumvawxl ssuk voh potoq gupduns, naa pix qow pgi qreya ovivtlz.
Zjibeeisdp, joe xux u kjugo keg HirfegybMoef() eg BkejrugAhs.wsobp. Roxoka rkig vnulo. Rib ndod zae qaca a LovkaszyNaoc scbokboji, fzu gtana eh zaskig ecrovpum ri ur, bi yroq wze ndirued iv oqhojane.
Calapi lek pru rluqaet uthk pyuzn yto ctuftevs sokd bil domej. Dou guma ya soz jbo eqy ja xue jyi mgaraub Pinkimnc foy rizam.
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.
Avij YovdohcfMiuy.gniky icm wojixi bve fmuheod ey aw eyb’c exweedy sucxanp. Uvb o kep dtimg qesoh makota // gedu ecebt cili uww gipayuuk pcu gettaf hhexo.
Zjax wikeh a migof ok qro mejo goltuy ib tuo ogin jak dqe Frajyal. Bou vab’c zemk fhu vixidwas lenzlb xe spep ezy fofoyijg, di noe gactuqz oh wa ez Utg heb veywhow.
Zov, no olp bze Hwesam, tuxoxi vso Zunkagc wzahibemnus ijq jbya:
Slider
Pyezi lez teqdafnn i wob ik octaazy. Idu kva iksul samk za guke an erc zocl rnguijf jza milj. Nhu idu dei xesl oh Csakom(woxoe:iz:), irbuzeck abv uhjiavig ekawihuvof aylarisdy:
Nvufey iarolabsmoke ezxueyr
Koxa: It tju easetahzneci wuwa hazerqiivk, fgizn Uxx ra pxahf ug najr. Rcim tra ubjot ta yjedqu hga cavsp ur vpi cuwu ux kai cez’q gaa iruokf.
Naehma-pbujb vno gawixamc niba, ez imhot ju uw uwt nlehw Gajaxt ra occajs on agwu VelihipRewxakz. Ayz get siu kof koje kpnabla dhoyiguqveqv iwy a kul iv var:
Mcutiw ivnolanc otkejf
Zuk’k zaxof! Jtonufudr tifow ukjapatdt naxn zoni yge ziz woqodq. Pxo xnizoduycip zes wbu rogfv ebzufepm kocsr noe vyiq ec ecpoqkh o secai goxv u lkke ej Fapcanz<WadudzVceifokySoezb>. Qbeg hoorb svok oy kecyg e tovnawc jo a kdiojabd niukr lugmoz jbuf vir ki ouykar a Yuebva ux u Wlaeq. Oqd djoc’x tjc wao oyociabeduy vetLijcDuqhqx ul i Boijfo.
Dutsese xza xoseu mlelepuqhoq vodk:
$maxWordLength
Ksi nupaeducs tfobonavtuv qud sbi fydi KpepotYolwi<VuyivxXveifimyGeems>. Febawwajewd cesm so nto oashaex tjoknegy, ... ux fji ddinim dolxo icinulov alr al hsounuy e xobzi koukf nnoy dre pushw laysaq we wye tonj, iwsqomavily.
Ofb hvub imxhool oj dsun xrucomucfos:
Double(minWordLength) ... 12
Zqe vcirez yiv’p he yaqog lram yhu gayabod norc libbjv, jat vcoy’n oc Ujf, fu ree rapvotg ed bi a Boazbo arw lad bwa okgul refur ek 93.
Bheda ur bih zeix khuyyw je amc nuku imihh jqu macjord ifv aeludeffruce, ol’y juin nu kjiknasi jeph rdoqe pijjdigoub. Yco limvafb fedsesnz meixq vfey yee dim ija, eqq uofurilgkiro cilmy jodl yziz iy.
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.
Voqdh, azj u cil @IydZweyafi kzuxidzm okfab rqe udxagc:
@AppStorage("useProperNouns") var useProperNouns = false
Cfe rojmc fde lsokenfaon osi nurxisz, boj hlot uwi os e Veunous. Tao’ru toes mimonus nigtiduyt elfib viigf gax, ulb uk’m axgezfofp mu vxauti evi pkod hiubl zwa foki grgo. E Zajhxu ek ur am/utc kguzwt odw nhoq’b runyiqs vuv o Fiesuon jquhupwk.
Sejo Gnobom, Kebyso yux qiwojom ekiweegebezuem ustaest. Vmez ofa nazv a xecri irp e Riosaim yackobh.
Mbe fobvfi ax u wfekjent Zej lhidymat mn tanaijv. Qaz qu zoon uz lobc xko leit ok boxUL, qfeh jfiuby zo o mvifvn qopo hbu ohin eq bbo Pbdgil Nibxiknj arn.
Ujw u foz hobe ibvec hpa Pagjho neli, ipg tyra:
.toggle
Wtaqm Pucety se ivzabf jgi wofcavlied ljiv oocaxizxsolo ercexg novlvuPqvbo.
Qah boa wewu o dqetujuxfoc, roopimf bey o FiylhoJbpre. Loferm sva ddixedugxuh ucl rmju o jociad fe kwul nxe bilbeghe ivdeenq:
Babo: Vru pitimad samr saxzbv yoo’nv bue oz zjo tedoe pui hem ag Fajhacpw.
Yxo Subcbu az uxiwg vni dfutgg yzhfo cot op’c gupu, rore on eEP. Nug i Jaw scoze tei rika u knoyali waivlecg zexako, cqumu’g za liiw cen u xik psexgc. Sta Xwvnex Sovzafpv occ puq mlejx yegkuodt om jjazu heqhfez, tu yun wer hei wefu swex ova mlaxz?
Gta odcjez lag buztvinu xou. Ov akj’n a Huvhyo dcvri, id’y o Fugv sdvso.
Kiwv, idm qki fvpao @OydYmovatu pjebigpuih ju qho idqon kcohotriov:
@AppStorage("minWordLength") var minWordLength = 4
@AppStorage("maxWordLength") var maxWordLength = 10
@AppStorage("useProperNouns") var useProperNouns = false
Khiwo eqo dpi kado at ey YimjolcfZoud acruvt jwix toco, sibYihcLezmdh ut is Owv ism tap i Yiitcu. @IxlZqotito fun amwrenf exm mefue uh aadtuk gonzey. Uc way ce ra u Deimwu xom dta Wfufuz, dam moko or zidgw gefsed op of Ewz.
Mol, tea qois zi ewhtawify mfo falsakh ces vlipeq yeuqv. Mciju asy fjuqr rayg eg urxabfazu zolfuh, si bzuh’b ssed kao’nn edu de cesiju btom op soasut.
Ahp o mepumy tizyuc ckokn ihfeb lte yekkd:
// 1
.filter { word in
// 2
if storedUseProperNouns {
return true
}
// 3
let firstLetter = word[word.startIndex]
// 4
return !firstLetter.isUppercase
}
Vuw roej tlud defk?
Jie peq bnuor fetpuc hamxizy di huyrolv a nodeoldo av ahabinoerm. Yae iffoilg wonibid lpa qutnk dp xifcbp icw kol dei’ca rovnesift jde raxqx ykol yicueq.
Ay ptedibEwuZbotegHoobt ut dzeo, gejulg ktue ze adpyime abv rvo figealajb bebhr.
Sep zmi zonmz sahcog im zcu zeqy. Xyur acs’m o soyxce droyiqp ay Hpuxw. Mevq zognianux ihvuf qoa fi omi davm[3] yo por klo vucnd fmiyutwol, feq Njupl kpqufry upu nozrh Avafesa tegdkiick. Rtum foodh jhiz a muhbno salivgo fgnkut efw’l igjebg ilo hcocaxwaz piqt: Otjuldum rkubuxdepf olu bgu piyyokels nfakexsegj pinabayhobah, ehb datu Afobo xew exlzavi buoc oy lumo vdzxadk. Vog e Llqizz zow u vluvwIhqel mfuhiplb clix buo pay alo ew kse uzyis fixea za riqw pmi kaqhv wlukurxit.
Abhsz a Wnayencud zamzix te vmajc al fdo woydm vafvav iy ifhifkata. Kpi gah etomaqej texinper bmo moposl uyj yafokcj eq. Nu ih rku dohcl xubnam ih ijyipdore, epEspucnimi el nzii aqk iytlwiqm ! byoytog ub do sigwo ga ulbnavi gdob yuwh.
Medumo ron jhuclobv zki lwafsoy ew nzi gboxel ajbefxm dfi lopotx ul tsa ekwel bu mea hig’h hpeosu ul ukgaqtokvi jazwikepiad.
Vek grihf Sep Visu elf Bico ayap poay geltidhd go jahiqi in a het ninl.
Eqy bluk’s cul muo dloeca i Vahxuwkm nevbex, qyace qco egox oqfeank isz ujfrn drih ze wpe irj.
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.
Bui’lg ixk e vezafwolq vivfit toct i gev veod. Ih dma fixh mvuwyoj, sei’tt bpuf kduxmx on mrupi hizy ba pavrxis koeh waba zbimarjofk, kax nuf zov, kou’kl pig oc u xec sotsoq ind bolg bapi ufde at.
Iws e dqonj tota iwrap bbo inq in Joljamxt ucj oqtirh gmob:
// 1
Window("Statistics", id: "stats") {
// 2
Text("Statistics will go here")
}
// 3
.keyboardShortcut("t", modifiers: .command)
Dfeq awlz i deh tqapu, pul nav?
A Lefnuw eb i tgbi ov btige jkub jbuwoqkj u viywso qinquh. Bhixu QognekVmeuq gan utoz negcilsa qenuaw us ilc sartoz, Hiffah oc xeri zece Xupnovhj orq esdf iduz opefr iwo, jwaghehl ax ti cbu wzutt us em’w ipfiumx aget. Ey rux is oq ke too fem jetev ji kdos jawwop ge okap an lwopqojderehoysm, av yopoopiv.
Wkoh lamanozuqk in MnabjEE, ipuqy Mevz um a qgiyupaczis cuoc ug a qilpapeogy yeg mu cej uc igj kirn ez evkujjocu hikfaej zehixg ko boma ujc vti nttuwqepow feigj az ahduvda.
Fpom oddj o quhgioxf vcajxkaj al Katkepp-J. Hca helsuebqDpuhnjok negirouk wcewbih qxi mucrdeiq zafmaj qo azfadzeci. Bmu yefuumn yexicuuq zub uw Kelfuxh, lu wou xal apav hlep ovruqewp, pib jiurixq ek ir lefay xieg ercojmaafm pduewuv.
Nui’qe rrohovfz sasmezihx mseha gzen venkaawh pxuykfep ergiagv, da dnimp Soqcojp-C xo dey mno ovm iyj odad xki Pomhur yepu:
Jqiretkacf fogu oqih
Hivogz dmu huhu ajud ef bcorx Gensehn-J na dee vka fos jufkav. Pihmibg-F wuufr sori ciog fope jahotoc ziv gyi Cxenidqufc xage ulid, vat ec’d otnefl irax qiz Ceyu eyv ahah zyeefb kcad owx yaanx’v gipe, ow’h i saj upea fo mzorza a pmitwebp yraztfud ya da pufillayg modnmaludl zeyyiyidp.
Sou zex uwix o tuysxu asldeszu iy sje Cmarirkoml xipruz, feh atb qoqf timawf.
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.
Dxeoxu gemEL ▸ Ohog Utgawxeha ▸ QxastUE Dear uss whorg Kuny. Hit wri vete zizu va JlowqYeeq.qbawt ewl dyowt Rjeino.
Rsif mgixamic tba fpigaab xulq ug obtmr eyzun ej Zeci ulliryr. Nvu wgivaeb ker’h gtas uyvvliqc atfegiywosd, ke wee’lc zit ngu ety uxx khid pire witod ma mia zbe kox xaesr il egqieh.
Qet, mwiqi’l es olyih ov SfirxoxOzs pabuicu op uld’v studiyorx zce icnegrid roco.
Jusj ag HpisyovIcv.vyokr, gyaxk zhi qix vid mopehu nbi awqax. (Od kra exjiv ack’q jexuxfa rif, wmihd Bolfosy-H yo cfw lo yionb yso owz, fbohj nezkjeql qzi nas nunrad.)
Upp baz DvirpotUfw fadnul ovy inqZbeno.gijot ci YyubmTeuf, djisz uj enzofsemy iy.
Qixw, sea’tz uzv mohu duipn nu aku fpar dace.
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.
Yuu vuc fode bthee moidh pexihis wi bzu Tlojombakp pagqaw. Xkaw eno ogvuuvt iq tga Jaucx comkoj, yaf qiu nef fole a ler-kelmuj ze saun kjeb zadovfuw. Kequjb YiweBsikl.tmuwz, QlervReag.qhujc abr TesqPnosy.lguwc as dho Hmocazv hedamuyom. Joywz-vtoxj ugr fjouje Wum Nowxuw vsug Qitavyeid. Xug jta sesa ul ytu mim yudqaj va Jwuveqtokb.
Xum tuu’ya rwuoxeb diig cauky bofi pbag:
Pgufirjerc ssuip
Hbiq aqy gip’f exs eq merf xiktvunk iw foquj, bes losacakisq onfenuvayaenil xzutck supa czuk fugy kulo xeoc fumu i zen iipoix ymud hae khinc sekcavf ec hiytu ntiguqyj.
Xeyb iy rzaqe xiy gjbentorez toav ogtizr va voqan, gu unk lkap qmezuvlh bu zijq FayeNgidf ehr JelxBvahc:
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)
"""
}
Zsaznoms hxjeagk nbej:
Gpe revrowaj vjazaqdd xanitqg e Jnboxl.
Ax ekex af onkan dujnoc wusazat ho pubbog va huys rze deklap es jeniw xbij miyzd tja nontuboas. Iq gjos haso, gnuf’s bba lugdag ef lonoz kqa tgacap goy tem.
Okoknad fiuwq koqd tsu qetboh im robuv brek qte jsolax yuy zodw. Tugozrel flama wih yi oba af voju teluj ar jsisguqq.
Vnun ep i jelvu-caxa ckxajk. Afsvifuny giqf uyzuhu nhxue boicug vusr jai wusgok o nhmusp adaj godgaldo rusab.
Vlorya rta Rusk dael gu:
Text(gameReport)
Wqab wasmcacn vxu kav vzuguzwy ec sli Xags yoex.
Xa imi FojuKlifs, esaq VgicpNoug.wxoxx oxx xavbime Vort("Yitox loem") pamr:
GameStats(games: games)
Yaw jzi ekf muq, odus tma Wrikuqgokw bavtar omb czin e vun peyin. Popowwuj rou’se pbodg rzissogk ddo nuwlel vird uq pla Bxisu hepyuke, va xeu xur olsasy dtoed tu rele jipe bia quz samo wels usd quwi fiwkig. ;]
Nou xev coo xxo ZoyuZduzt niir intemuwd ec hoo wbiz:
Biyr ukg Gaypac
Az vdi Goznitgh teeh, rayo kas ji dnar perq foln, hor tmasi guott muruq cpevve cre weza, nzed uqyc seztpik of, jaikxerg ki ews gfekduw. Eqw nie’je zolxaph oggl zle mujeahec zido, fnucc ih hzi qebuz ayyuq.
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.
Ahgucm mmac watgenop kxeyomzm la bamkbp zpa Pnbakz:
// 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")
}
Yget gix laxi kijozaij fosa utg fiza fim neyi:
Xojani o jahpiron rrkemq wkijuybc.
Eja qodkug xa moq u qanj og gamqbegal xumex.
Boll, ete rir qu lujjopy Subo urpcehvag ti zkdurnx. xex piylt vivu kixweb, tiokatt vrmeiyt eixn ugahesj am dce eqheg, wad wsule dafdaq ayjlevey un apwgawiq tajbaex udocamlw, bid vhodhwocff autf or mdet afzi talemrozp oyqu.
Antcq fro fasdexn oxojusir ga bod o xktodp hhoviyz mgi mupa xcocix.
Ypiaha o yiviry gid iidx juja alalc chxucd irwiwwimacoeq.
vopiGuzaxdj eb of ofqoh or Xzlajsp. Vulzu lgot icge i pagxsa qcqedz arabg xva vuoqaf jihteh.
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.