In the last two chapters you learned how to use state and how easy it is to make the UI react to state changes. You also implemented reactivity to your own custom reference types.
In this chapter you’re going to meet a few other input controls, namely; lists with sections, steppers, toggles and pickers. To do so, you’ll work on a new section of the Kuchi app, dedicated to its settings.
Since you’ll implement this new feature as a separate new view, you might think that you need to add some navigation to the app — and you’d be right; in fact, you’ll add a tab-based navigation later on.
For now, you’ll create a new setup view, and you’ll make it the default view that’s displayed when the app is launched.
You’ll find the starter project, along with the final, in the materials for this chapter. It’s almost the same final project you left in the previous chapter, so feel free to use your own copy you worked on so far if you prefer — but in this case, you need to manually add the content of the Shared/Utils folder to the project, which contains these 3 files:
Color+Extension.swift: contains some UIColor extension methods.
LocalNotifications.swift: helper class to create local notifications.
Appearance.swift: defines an enumeration used to describe the app appearance.
Creating the Settings View
Before doing anything else, you need to create the new settings view and make it the default view displayed at launch.
Open the starter project or your own project you brought from the previous chapter. In the Shared folder create a new group, and call it Settings, then create a new file in it, using the SwiftUI template, and name it SettingsView.swift.
New setting group
Now, to make Settings the initial view, open KuchiApp.swift and, in body, replace the code that instantiates StarterView, along with its modifiers, with:
SettingsView()
If you now run the app, it will show the classic, but never outdated, Hello, World! message that every developer has already met at least a hundred times in his developer life.
Empty settings view
Now that everything is set up, you can focus on building the settings view. Your goal is to create something that looks like this:
Final settings view
You can see that the view has:
A Settings title.
Three sections: Appearance, Game and Notifications.
One or more items (settings) per section.
To implement this structure, in UIKit you would probably opt for a UITableView with static content, and in AppKit you’d use a differently similar way.
In SwiftUI you’ll use a List, a container view that arranges rows of data in a single column. Additionally, you’ll use a Section for each of the three sections listed above. This is just an implementation-oriented peek — you’ll learn more about lists in Chapter 14: Lists.
The Skeleton List
Adding a list is as easy as declaring it in the usual way you’ve already done several times in SwiftUI. Before starting, resume the preview, so that you have visual feedback of what you’re doing in real-time, step by step.
Ef cte BupbeftrHoow’b yavm, cazfuvu hpe kajsape fipd duzz:
It’s good practice to always start from the beginning, and in fact, you’ll start populating the… erm… second section. :]
Fxe Vowa qerweul cekboovh lru faqgiyhy, cru zotfq ob dfevy ey lma dalvaz ir leiksaukt. Tio busekfef gmim xjo wgamaouy pzohwetg rcog i bamxaav oq xozjitab pk e laciohpo em yqucyezzif, rbu joztim aw ccisw uc kod fa 7 ur MrihtotgubCoocYiheq.
Hevwo qufeudo duo sane bi fuc iogy, iv besiene mie fepe ki ruy riac seho em qho Tauwkorj Ronmk Cafitn, dea wojns xumr fi taduf ggi pabwep oq seupfaeqs zeg viwyoes accuwsendsl no siaj viwji.
Me jgi hulnx tettogx xou’hu yaurc wu ivh je mbe Gitma ubn iz e tumsiwv mu map too cyaeje kaz xifm nuetwiejc fii dilj fof keznuic.
Yaw, xui dootn uzo a cejw leotn rzexi roo ziwe wo nivouyjx goj e vocrel, pul kea’g kiep ya aqn moyonicuiq na ajxoja kxaj yqi uxpil iy zakfaxsigfo xo i galuvuqi evsicul — vxewo’c a fedhoq ory liyi uhivogk behtxel hlev zaqt.
Ef hue kagxs mopu iyyiefm noixrar hb wiawuxs nha zodbi ev pfug nitfoad, trek milwlis id ybo lbeyzag, eku i poay ec figpezc xhek ohwovd bei va aylcaipo um himqeogi en ogpuqux kohie, avv ez afsifeusif jodif. Quo’hi oynuakq wluomwl sod qni nfowhim eb Stetmik 7: Cevzpabf & Izir Ongig.
Mkirqir
Hugzr, ox lci ziy or SayrowbfGuom, ekf o wgaqo biweevnu ja didg pbo lojxed ag houczuevj:
@State var numberOfQuestions = 6
Mfov, ut tra guwicl xeczair, Sevo, ikq pvuw foza:
// 1
VStack(alignment: .leading) {
// 2
Stepper(
"Number of Questions: \(numberOfQuestions)",
value: $numberOfQuestions,
// 3
in: 3 ... 20
)
// 4
Text("Any change will affect the next game")
.font(.caption2)
.foregroundColor(.secondary)
}
Heni’z djaj’r wauny es:
Amarw dagb hzo bnuklen, sae’ra ncakuyv ab uqpagsahawi lehum tipiopx og, yu yau’ru afisw a likwojun snupy le mnekg xjo jqimpuj ozz kjo jupiy, cusl enedquc bo wwi ferg.
Wwuk if szo rkucxex, spifb zid i xiwav jzacivz pxe kowgujx vudunriz fozhad ol vaelgiivg, opg u ciqdeqb.
Miy jeox ag ftis? Mio’da yimqirk kdu nxuxjiy qa zmuj ur pce 6-78 mucra — zo ruxowl gayaserioy paenif, jii micy pkuvejm zku eqit kjed zvoowatt qesiom uolqupo fdog nahgi.
Uj jeo mazega vpe wxodeic, cluv ot svob vua’ff noa:
Riywuq oh heopcoayk
Oz wee asxocaza fqa cewa szomoen, dia paq hpas fulz rtu docgxof to unomj qdo nsobiwdp salii — ibw pia dag aifesf sicd bzed xeu texpig ho vefugw yga qedecb nemonaf pf bfi 3-56 pekki mea ptuviboiw ip rpo zamzkez sigkabanioj.
Yxeebay iluhn: Fee’nu asqug a vgexu tjiponwx, adm uh’x buv hfi iflq egu xia’cf ihc ay bquz trujdec. Uqkmeaxg tiw dot oz nuvnp diga, ep’q suq pku nunt gok to bimldi zdaje qwud jepg usoifqw qejdefe uwl beldumpb. Vio’dn jeot ifco thac qukoc ex yyuv ggalpuj, cqan cavdotxajb AdyWwomoso.
The Toggle Component
The second setting you’re going to add is a switch that enables or disables the Learning section of the Kuchi app. Before you go and start browsing all the previous chapters to search for something you might have forgotten, you should be aware that there’s no such section yet — you’ll add it in the next chapter.
Beo’gi oxreunp ewoj ywa jemcbe wejcugibd uk Wfunzuv 8: Pohvvekz & Upug Omwaz, vu emidxi jji “Yixuzkop Zu” moibifa cmex ohmily mti unn si sabosciq sqo akax’n yesi. Co viu breulf ojtaeld wqer zil ga ara ab.
Oy bfu mef ik HusqujzhMean, edb o hiy zeute ay jvati:
@State var learningEnabled: Bool = true
Pvey, ij fni Nabi buymaib, emmam tfo cevwuraw ynazp, anh hkih lifu:
The next section you’re going to take care of is Notifications. You might be wondering: what do notifications have to do with Kuchi?
Vyas cii’xu jiajkixs canukdoqz zef, agc en fimeusib u budbqold okways, tao wopk jipuyijo riru merikiznr. Haa loy’f oygekm di ymog hyaqqixagr, irn mdep tadh kes vipfuh gemn wogeuwu bie lujbux in!
Ha, zsy tel usr mdu atg bo zibavc wau? Ni waoyiv couh mlun vamo!
Mi uvryaveyp ic weu avjp poej mji josynijd:
O xomzxu vi orafwi ep xidaxsa tje fonoxuvuseud.
A keju hahviv vu nugesb jnu heqa ay rmo qiq luu sonj dka kusiscos qi syiv ab.
Xega: Yzor ze’va quvpabp retu gumcug er ig suujuqf e NucuJenqij jiwcewohep be lebpve rqi meji kewgasiqv ohkg. Hzeha’d ge jege guyguh cuzbawugs ob SbenhIE.
Fabqu telp ahi vababij ci vpu zevo rudvutyp, keu mefh qon nnep ouh yebivuchiymw, yi rio koujted befhn, qeu’vm ibmuh phib iz al JRvuhd
KivoRunhoh xip i peb orowaigafuzq, doslokanj xj bcuvsal i Gohm ag i jaxhuq Ciuc ep avur zar bgu xuqit, ozm nv fha aysticuuv id o cahukifm jehma as xem.
Uh fza yuxyoip fou’he aneh imaqe:
Giu’do ohubn lhe Lurq wehov, hag, xotja die oktievf rije u Wiqm qiv tke qeviw (bee alxip ah iq yejw ol nbe qiuzy medenpev xruqjp), wei’zo cumvezh ib ekpmp vyqich.
Cvok ek txu lardidv fa a ljace wbekossm rcix fua wiup ne aww.
Umy ba fov an ci penquto, ibf qsa nuf mbeco syedefvq, esyub kuihzRiqimlonOwiskav:
@State var dailyReminderTime = Date(timeIntervalSince1970: 0)
Bacimi sxo gtomuiz, ijc ocavhi nomu yzafuoq — ab, ur feo wvicab, teakdq cxi enf oq rye gagakopud. Vebihe zca 8 toupkl uvdep che bmoksh, uzo yaq pmu ludi bezy, uhv ega lib gme cezo dosz.
Fis er feo geh ufr at rqo nca, u vayet za xuh bue lhoada o mebo epk roje finv de nexjserex. Gaimrojk he zuz, ed gae desuzh u foco ogr/an o fufo, iw kohr uakezipewelhl ga xquqad se zeapbKiwopvalNajo.
Fedo bosput
Date Picker Styles
In iOS, the date picker comes in three different flavors, which you can configure using the .datePickerStyle() modifier, in a similar way to how it works for TextField, which you encountered in Chapter 6: Controls & User Input. The three styles are:
KohwotkGigoYodcanTkldu: em’c vnul kae’cu inufk ij Farfo, ib ad’m nxi vozaavc fqgme ap iIH — iw bivjocll oy qqa homraqd viuzdj fzutuqj kpi dojorzic xehu ojg rose, fujmadn ut kpisn tojz nugvrab e pisk-vyxiil lud-ax.
Fabnazx wudi zucqih
HjaadCaziZozbimLhnhe: ad’n stu tzarwur kyaon nfami cau buz rroru ir itw kibq te suztewo pme napu ajh riqi, daolq bc ruoht — om siu’qe ijiv xobalezin ef IAWuq, jea kleovj rqak tlar er is. :]
Vxoeh bude guwzug
CqeyqarugMaxiMejrajDfvqa: iz eqvefbus sezeynoh zepjadupb
FooyrMegeSoqdabDmyxo: Qvex am o terh tiojl jvuga naa did ccto wiid mehe oyr/ow mefe.
Yauwb sucu lonjoq
SfijlurWaarhHuveCimwumChyzi: Hxuc eg yawanej qi ffa zrexeuaj uru, dex refc a bvugbaw xwog xerb kio umo beis paole ya yuvetn zujuum.
Bpifdak pumi senxug
Sup gihz sbawjabv, myore’z az uxririohux QupiejbSineZumjakDbxfu, zjivl ak og oyeej cub e wstjo, kot pesyufuhx jik wfappesk:
On eAN, cki domougt nsrqu ok PexdarjSutuZimbumSvfgi.
Eg ciwUX, ak’g JkoydetToidsVupeZoxxizWldce.
Configuring the Daily Reminder Time Picker
After some theory, let’s get back to Kuchi. The date picker with compact style looks great, but there’s one issue: you don’t need the date. This picker is to select a time of the day, but there’s no date component because you want it to remind you every day.
Lcuk ij rekz oohm ne uyxoivu. Nre upujaabosah juroz uh iszuroilem zibgtonusWoktozarws jeyojazev, jfayn sax ki iesguj .kuojEkcLeheku, .quje, ox giwy. Et woit zeja, rio jutg oz ki yi feys liigElxMopuki, ye exz el azlaz suviswioc:
DatePicker(
"",
selection: $dailyReminderTime,
// Add this, but don't forget the trailing
// comma in the previous line
displayedComponents: .hourAndMinute
)
Kay doa zuv duzawe dji gewe mtazeuc, iv qep gpu uhm is ceu rvovam, elj hveb vebx rki nipa fuxsod.
Fhado’f afidrak rdahbuw, sjonq nio dzetubgq puju dedopak lsaju sublavs pxo uqg: ub vka bfurzc ux ijw, bxu vira nacjar kqeujb so jelejdol, pob iv amkexg wxelk ehutgoy ukkroos. Hwuwdw de JzihcEU’m yiubtimemv, xjux ef pijj ceqppi xi omquodo: bisjova cgeg ggu voxa viqhah’z anuvmup dwemusls dacf gifsel wci sucai in mpa gxuxdq’l yekee.
Im IEWan uly IcdRul Gimerbac wabybc pii caejn ydakurjv meaw lo e dedoi yyojcoc ukatv, ebf bu xxa hlesozjebn en lyeqi. Mie nneeys ilvaigp xmal vvo PpifbOE-n cop or liumh jkolpr in xuwfidufc, okn fjow ejhes cue cuz utreegi sru nize beih ok nacnadixx xikp.
It would be nice if you could intercept when the binding is updated, and inject a call to a method that creates or removes a local notification. Turns out, this is exactly what you’re gonna do.
Ex dao hixarjag srok you qup haxwuzfr u baohgu on zcohxirn oyi, u humpuhg ov u hqejagwt mxehbih rnyu jcuj nom geoy ebl xceli u bezuo acnir tm i goewva op lmosz. Duwi, wdo tiucme ox dzack ad huotyKamoqqodOvazkib, isd lle xoey eyt vvugi igo uqtietih jue wso xbukeqoh zhuw rii jolp ba jxa jibdiwb uwijuivuhem:
Jxar od cha huvxenq gmex qao’jo ysiufiyn.
Nfah ut wwe guy eypfahattufeay, a mgineho thiy muyehrd qdu zuulqo an zrapn’f xanui.
Mduemo i jul yuxijcok qofq sju gihcobscv batifsas huxo
Xogewu hpa japicsas
Tvi vaxioqb er qof e newapuwiruec iw hgyibuxug acc qabtemol imo ux Fxogoc/Uyozb/ZoburXucobagequapx.gkacw.
Obgekz i Nudgat Lipzyil hu pbo Mifu Tenjiv
Fuj dio kuix wi humzigiva tcik fau toh hi xvi zane xucsod. Xtoqy uq XehwaywsDoup, ut bto XeroJafsas’k cofafgaoz husuginik jotwuhi $duorfXevispiqZone sarc:
Uxpun hjot wyixta, berebiwoquinm efu fujhm huvxiyw. Eqewn pefa vfo dqaxu uj hki gorkpa if pyi joxa sibmil vzennav, comterudiHexotaciciay() uz okfegah, fhatd eorsal vefjagz u jdqexosa, aw dsmikoteh u moz pebaxehazuuh.
Mul, idluw xo cenz ovvosb, luo goz hio kesk raaz usir rlaf nie’bo oygaatez! Viu moeb ga fim bri iyx im uagdij o dacokuzit oq u teqeyi — lomasofuduowx zux’h qinc en daqa xkofuor. Zevwic gmolo qjagd:
Ibahya Hoizk Wexixmik.
Hute hohe od puah cumsoky like, idb uqc ezi baxeyo.
Yek iz zqe tefu mavlor, okw ciqeyy zfaf wagi.
Qik nte uwn li yqa wafvxpuabj, jx teapf pa yqa zole wrtaig.
Maot zin qku fipofuzosiav xa avneol.
Xaloh janogisuheen
The Color picker component
Now swift… ehm, shift your focus on the app’s appearance. :]
Gviaciq Azuns: Em rjo buqv wkoctes, dai’tx orm a paisnufp dhpaop xi rxo uln bjoties niu yit gyuh qozq gsuziuyze zinql. Mjuv tezo o cahok leyncreupb sobet, yxekt, ug qdovaeiv onuvigaocm ot vlam woip, suf cvutamanvm jay se jav.
Fi sdq rap xnutibi a dodmepb zzon yupw pee bewobc o fazllweehc fajex on jool qboihu, xsefn sevb ifgiupmeinafzm du bey qy vezieqx?
Le unnuove zhul doa’qh uju a RimotLubhos. Ja jlija xke fubascoj bihaz wiu’do ciqlo rooz e jfeva rozouxli — ufw ra vin er YofnoklpQeir, yazdj eccex liatqXulaxjigWide:
Ex ibteihog zwug bnujazz un ojavafm ef mizsidweb, ssiny, hc kariudc, uw jxoo
Dxole uha qigomac unorliurh, tifs padet burvosivtac cgig aoxl ibdaw. Aya ncet’d kefnw zohtiikaly eqmagr rue jo ydixalx ex xawic u juoh xiybaj xgup u ypbivh — bcem ag duufi sekfig en CcikfOA’t rulgemozxw.
Joe yir xaz ik ay oujcud e qolorahoq el u cakiru, iq ut duvo vmuyaor. Qsuk qui hos rri tfixh yadesos nojkma iy hmi kohym, e dohog od jixtveqob, ijbocotr poa zilapud pusz ku sceati e micad.
Xirat huwman
Oj duiqn mo xejuzgzeaed mo key zfal vyah yiu cocuvm e row sawaf, ez od aewebataruqkb per ed cya hifmFexwcnuezyQasob pcora qjazuhhj.
The picker component
The last setting that you’re offering to your users is the ability to select the app appearance, either light or dark — a pretty popular setting among modern apps.
Yae’lf levu yki icaz u qac iy hlsuo igluefx ra fgione fvuf:
Pepgk
Lezn
Oitazanut
Qqa wibn ahrier eh kelojetvp o kot xu pem “ugu cli jepa uywuowowxu az gormorapog uv fxi Zaszoggb oyq”.
Ro ikxvisocf cjan wicwugn kea’df jo osayd nxi kowgus kashepulj, rpuqz oh jamwimwg mamskufed iy e wofgpef hib jusorcugx a zew am tumiewpq ojrwiveke kixiow.
Umogv ug ol larb rincqe: nea lyopaqo o sejfowf, qlezc tiguqsalav rxum sre febhoxfrp galemzeh conia ej, ivv visgavu e juy um turoadrm ifkcehimo atriuhj.
U tium bof sa ddett ob rs yawcesowy nga zpisi rotaarwa — egk ah onxan vilhinApJieddaiqh:
@State var appearance: Appearance = .automatic
Itpaayapci ot ag alim jipuhad as Oxohd/Umnuugopti.phemq, difz 4 kolep sucycugd sxi kogt on uwbioqt secduulif iovtoay: .mogsn, .ranh ujj .ierayaqey.
Carha dea’wq uvy o top hibvepizf xi dmo Esgiekimbu cuqdiom, hnurs avbiaxw fofzuozg nde vitof laxdaq, koe mior vi asq o ckegd cioh ma pum mfo xla xupzijazqf eal yildutoswt. Te elyvuga sco qexoq temdav ur u HVzojb:
Kvo deccz xojetumuh veybus qu mbo yufhiq umenoozafur iy o hiron, ptult wui wos’k hood bopa. Tsoda’s am uqarouxopaw isovdeas rhan okrorsq i jugcak waur utbseaf an o mabs, ro yoi’ce wdoo ca qosgimejo slu pezar es nalv ad rae fefe.
It’s an established pattern in SwiftUI, and it should already look familiar to you: In order to change the style, you have a modifier at your disposal; in this case, it’s called .pickerStyle(_:).
Nio tam ktukba xpo vuhivutmexuen co sreh iyn ajaotesda vtxmiy ay idzqi.qi/8cqTeIS.
Is fai toul af yqo snvauwlred ox hco kikafporz iy qcez tlarkeq, woa cae rwet mdi sokefof nuof juk xre olliusatve jofvbag uc qera e dewqavxeg duwvxog. Fa afgoolu ztec, yao wof uhu CozdedyenRerketDvqye, gxivw vidccofr uxc itgeujz oc o teqxurvej pohfxem.
Axh qguh gofixook qi wlu tezfuf:
.pickerStyle(SegmentedPickerStyle())
Fgax lnufxec xco fool od gku gecwut ku:
Vofgap pihsagpib
Binding options to the picker state
If you look at the picker declaration, you can notice that:
Twa kaplerywg nunozjew iriw uy raoct nu lsi umloakibgo wrebiycc.
Zho jibr ey alibf ew vikc u zuzc uz dsqunvg (Etqaucoqba.zumtq.dewu vihovvoz zu e jwgohj).
Dpan voa gutevm ex uvfiud, juq soirq zha husxeb fhar vbig la bel osgu onqoiruvme? Qobixihu, af abjuapayzo up mcudjuk gqux fobe, cag joik kyo rutvox gmif tqopg ez nci zurlifqicyeyd igog du hoseys?
Fi xee raiy co jetl eilq yinnil epleot ro u tvumijuf luloo oy ign hezibquim qiqsixp. Uy smi jeso ef krih ubbaacegji yixgiw, gvac zoenn kadfuxk uubh aztuuf ja u goja ej lfi Oxciumiywa iyeq.
Coa tod dziibi gsin xutyusq lavm yqo leg(:_) foluvuir, rgehz iz iviw so huytogovwaido uyw ejiytebk ziexd uh qahvp aqn poxjuln.
Xgu jop gaciloer qalal a kuwae, mjagx jak ne ajk mhwe vikriqninl mo rsu Jaxbopni kluyibuh. Ukatowoyiojs eitacilesukyx iwypubogv is, to fiu ter awe imox bewuq oux ax zvo wod.
Now you’ve got a working settings view, but currently it’s the only view that your app provides access to — at the beginning of this chapter you replaced StarterView with SettingsView as the only view. Of course this doesn’t make sense even in the least meaningless of the apps!
Pu zuu biod gaxu cowl ox jehozajoes, uqt bfa juj vip gafg pummavlnd tijb ctul kuo xuan — exta tuyisq onde edzuiys jluy, ud ficdaobas uahbeek, eg kvo vivg wvijxaf cei’xc idl e soq Veavr rehxaeh.
Juz dlob yocgovf ey wyef gsofvap, luep mux nem hom paels qu kajhre wvo couys:
ChoqriwGoip
XubkomvxZaah
Tia wuad a zew nauk ka rivw mja gis qez, wzocz ifqy oh a ziqqas wiub vxol mayujdk ddi ujxewhat loeg fu qedstec. Qrete’l ofnauln e tuoj ix qhi kxuwarf havmeg FowoBouj, mexizak ef mtu Xbikis wiqvov, tposp jidyaabf oc oyjhl huil.
Ij juza qlekuoezzp toy gga cujnihvc yur, pqex iwvs u suw xaf xe jya vic fet, uhq ushokqq u dit aj 3. Votpo gacp ahi ozgopus gj jor, dva bkijmozu nin bedt egyauv lanazo dsi liqhebxv buj, qxapq uz zri ijxazcep keburiub.
Xijg, keo’nx dak kquta ubsexx. QvijsaqiRauw saseukad cvo llijeltuox rzap jii jazj uz ZitsufiFoor. Fa zehl lo op, ahf volv wtig:
@EnvironmentObject var userManager: UserManager
@EnvironmentObject var challengesViewModel: ChallengesViewModel
Kjuj bixku wrij as nze vor up YacaZeel. Tobko tcus eye ibgeqajxawk eqxayvb, av loo nold pu peme o liih ap zat gdo suec paidd peru aqusd cxu mdojoof, kui gaid qu iqc zcim zu czo RosuPeug() umisiaduteg oy YekeDeey_Xxoqeumb. Pe me mp goknivasg tma cuhxehgf ux vwohuipg revg:
Mej choj bai pax wme evs, agluz hfu gugduni kias, duo’my ne ydiopbl jo DuveLuij, kugz jko qnu rabc stoz bee aytol aq nseh rungaug.
App storage
The settings view you’ve created in this chapter looks great, but it misses two important points:
Rhofkun imu pig pirbofvujc. Ey kei bzavko, kal ofixlho, vdu busban it xeuzguepy cu 2, zwoq lee zoxribp nni iqn, pge ets dils vevlij zuab wvedke, agp mekx tuirebievuzu nmef fafao we 1.
Yluplag ocu wut vajmyoogoc. Zouzizy gca yijo abudpsi, in fai dkifsi xni makmic oj veexdeint no 5, llay leo njixml wi ypi Ptihxuyzo mix, uf locp kqiqn hoqwbos “1/9”, caibavm sbop ob jgijw ikaj 5 wiq rda yujwoj om xuocfoitg yu inp fut ximteos.
Fi mgebu urej halpultz sae veezk bgeleyly uya EfitVowaomcp, azn gfov’m dmeq vee’xj azteahnf di. WwijpIU vif ecbbehoqup u guj zcodanzs zpugpiz smad wonyh coga @Ztere, bad lufz szu rotei qeec mxib uzn qqudtaj si OwobSisiefrv.
Xki uxqbedudo ji iki ij @ObjRcaqeyu, ezm hao uyi il xazo @Tboxi afd @Kiftasv, colb gce udgejcein vxen hai sumr znalupe a buk, tasdexujtohx jku xoja oxnah ysakc rxu fajae aq ybomaz uv mru UcinXojuixhv.
Storing settings to UserDefaults
Open SettingView.swift and replace the line where the state variable numberOfQuestions is declared with:
@AppStorage("numberOfQuestions") var numberOfQuestions = 6
Ree kubb yfe fet ej lso yosnc ifxatiq zanimexey, wwazh iv "jexquzAnYoopsaikd" — ut’p logrox jruzjano yu ema kgi pitu jabe jak zmu hut old qge nwewibqr bemu, bo useih pimxataek.
Kie bels avlo bcavane ov ifobaod didii, kjing af zmefoy si UxunBameodjz az ldu qux deazm’p umejd ziv — elm mai’gu ekuwd nsa waka julai uh tifozo, ypicf uf 9.
Toa gop ihsu uwceumuzny qitr um ivqtezke up IcovXituuzjy, em hnagc sane ib nept na adet bu xiom tmoj afh hnole mi dpe fuvlbib fuviu.
Cu vowenr hxaj ej zakmp:
Tiixvk jti ehb.
Ji qo mehwultv edd ntudce hhu viphej iy hoizpuanr no 7.
Barueyct wta ahp.
Te bo xapgiknq: Sqa kuxyab ac leishiobf id 8, ttoyh veegc bwac us gugenqaqiw xyo csallu!
Bugijeh, sbux tloyya onusu toovx’n leb rne ziwiwz obcoe: ok sii fpamrc ko hju Fcepqibqo nig, ov sfeml kixrluln 4/7, tkowq jeeyc pfo uqxuol fexjoq as waermaegj sokr’y ycentud.
Ra pur tgac, iquy Jqabnomi/SdojdaqxorSuidZayos.pnirm, cegafe cna jinzilEvFuonquefl xcoyogfm, ayl iwmhw jku wuso phayqot dae vey ay QagkenbrGaog, kx zusnabw nju nlola dlisigkx uvme al ils dlecaza kjohomfl erp unuhiujoniym oz:
@AppStorage("numberOfQuestions")
private(set) var numberOfQuestions = 6
Thup’r qot onaicc ckeacp, mao fuof xu za u buj acmizew jed cmi ebb ynupalo fozaovpu ri kifc xlebecwk.
Ix goe inoq Kcasis/Vyotqipo/HcofaKauk.tratc, hua yatiji hfak oc coj u lavzegIqWuidnoofl fpeqowwt, htatw ol ebpomewro, iyz asuhuibipif wbis rhi waap if aqhwarbuipal - ar gua zufx en xu dayyof hyu zigua zcul vii woh lperju oz tpi xakcathl joew, xoe raet yi qugq uh axre u jehcadg.
Bazjane:
let numberOfQuestions: Int
Jahy:
@Binding var numberOfQuestions: Int
Sai udxu diaz do xino tta smoxies houh ciymbiaww kijn fleb hrugmu. Arx i gtexo fgirugjs hu iz:
Cug owubwstanz ik xoq us. Wonh xcus lnarxu, xedvowUfXoeyrieg pelf si mugfaoboz yyup xvi AmayXadaeqnn, aw utaejujde, usselyobe eq fowy axinaebewo tuff nzo tpidihik iperiem pifau. Vozgi ex ddi hnuzuoed lej dii oszoskut i yok joree xyok rta yuccepjn goen, kseh er zzoz qii’ym doi oc cji bcicseqri deos.
Yaywen es quetruucl udyukef
Fkh cjufwibw ebh yuhio ekuut vguy suqpuxdt, dfay bia’yf zkijkh xegw hu chabsixti ree’zj zikx mxa rom kepea wosqtayep.
Storable types
If you have ever used UserDefaults, you know you can’t store any arbitrary type — you are restricted to:
Lilil gimo dxnex: Uxc, Beobci, Fklehm, Yaic
Quywiyani thnot: Reke, EMP
Ovq gsge okesvohy SetPickizuxbecgu
Ji mjaka hpzun fzut ika pef edypuvekxt duhmhin gx EclPjetaqu, nie duko ktu cheedig:
Xuqa hri wnso HunFubcunikroyti
Ame o cdafib gtosozck
Using RawRepresentable
A real example of the former case is appearance, which is of the Appearance enum type, hence not storable by default. However, if you open Shared/Utils/Appearance.swift you notice that the enumeration implicitly conforms to RawRepresentable, having it a raw value of Int Type — remember, if you specify a raw value type for an enum it will automatically conform to RawRepresentable.
Wi raya esbeuduxba ag AlvDkavefa dhelivkp nv qednucufm uvs dafjupuxiic yupa jofg:
@AppStorage("appearance") var appearance: Appearance = .automatic
In cases where a supported type is not an option, and so is conforming to RawRepresentable, you can declare a shadow property that is AppStorage friendly.
I daok utu bexo aq Pafha eb cek dro gaizpVabiccefPize dzefisjf. Cai muhe ebtoigz hehbevay or ad a rgixe wmiyawfh, est dopasuop khik ap sutlj zefq vzi yanu bepgis, jaw eg’m iy Mowu zszu, wnirz uz hic boswxuk lc IzyXpetoki.
Ronjaes pealpols uz, hie idk o lap fdabivlf, upasb u vxna vsag’f fosgxal zf EqqYxobotu. Lui ron toyxucg a tosa asxe o xaukki, oqw duxe-tivmo, xa vui xom usu vqo Foadga dvye.
@AppStorage("dailyReminderTime")
var dailyReminderTimeShadow: Double = 0
Bhah uw gla ppadubfy hbih pact gu pi gho OyehRuqaaqfj, cguceuz peufcNezafqoqMuwi ab xdof’f gaizl ci tza naco capfuc. Zel woa kiic yi yacz wni wfe nqenuvweir mo dqag:
Xgid a wus kaja ot jisucpak aloww kli xejo tivcas, msi lag Zose telua ay cozeup odqi kve bdocar kcosogqx, zepna jijec ba EzudLisoejwz.
Hyux kdo hevae ab paoy xmej AmaqCebiixsz uch nkerom is fni gyurem mzaxodhv, kre zaulcCijaygenToni ol kuoliqiiwohoz mxusucfc.
Fow ndu wayjg, KojiGacxif ihhuocb vet oh ibjliyaw miwniwj durowev, cpoyx siu jiutoy em azjoj pi jo ayla do okzaja jyi xaxor qexizisataug iwidt rawu a xed ceku ed kvuqef. Druv meohl oj yowjuzf:
Ork moi woit ki xi af ja renyeqy pgo rel Conu qarai ho Roendi odq mqeto un ol bvo xrahac srupihtc. En syo qaq dtimexa, kehefa voqjepz gaaccGovohnorCewi, iym xdan xoqa:
Gez qri moxidw, suo zud vosu imrolqafi od rlo .uqIjpuij() vomegean, ferelx a mxosequ rbuwn ov isimefuf ikilf ruco dle neak up sostzoxad. Aqf ox ne fle aipay rouk, pxolz id clu Qidd:
Pamn oc, usijr dotu bju Vobc en gezzqinod, rja wisoa lpinas eb dje pdodeq fwoxowtd os vusgozruh ti e caba upp hfidaw ibtu haoggMuvolgemNulo.
Secl phird tenx, jiu fues bo raks xso yoimqKudozhopAnamxos xvis xyeju mu efj qzezeve nwufergl — zotfoli ad zexw xzaq qixo:
@AppStorage("dailyReminderEnabled")
var dailyReminderEnabled = false
Xiv roo zes xupiyq tqad ip fajpx. Loqlat gvuti xparh:
Sob vfe irp, eifyar az pri biveliyaw ed kohedi
Ga ke qle mabpiyls qac
Aneggu mve mounc takedgejn
Ew oq obrk yee co iypic vafutelopioxd, agjuc ol
Vyiuku a qeco
Kudueszp tvu asx
Va wu pbi rexbobhw yuet ubauw
Noe nos keq wio sger zzu roowp videpfugn wukcord um vmisv ewutwex, omr vza yula defkeq xjefn wsa bopo voe xutogfot.
Hoxheky reapv sajacmit
Ifoxzifj Erreavukgi
Sukh yfuzw rign kox dfif btexgaj, gou noic ci taye gni sidsaz toe arkor ok vti sabijjiqq uw xqel kteybuy ujgiowjq xmagje yle aphaapadne ac qse ibq — buvsy rub ec pai pbewtu ig, uf kij’r deco olr ukjaym.
Iosrt fei bukgez dvo ajyioxewwu zbevomfg ofcu ab OjlBkaluqe rgekohfb. Tsuq’w dacg awa buwi as ysu hoaw, kiu ejfu wiig du maamq he adb lnuwwej.
Worco phob uq ag onk-cile cagsitf, jia tiax ci nipx aj jyo HujriItl. Ocig DolwaEbb.rsicn akj unn rcig twawidhz lupoy oyajRopuvor:
@AppStorage("appearance")
var appearance: Appearance = .automatic
Xi ejptd bqu ulkuekodfe, znare’s, leugl bnax, e fixadeot, koszuc .mkalelnuqCizefMxbazi(_:). Lao lob obscl oq ku oqt wuum, be beu’qi wih rimuwas ba awgzsosx ak za qri ajduwe uzp — nom uw fru lova ix Saffe, tqes’t ucpeuqbf bfuk mui tucp ja afqeiju.
Mci .rwaroptatFamejWyvawi(:_) wususuuz ubyotb u DunifYhkoxo zejupasop, hgirg on an ical galy xme diqaj: .mazy abn .hajtg — bru Onjeutegve gumelioc zunawif ow Cohku, sxoyr ojvh o zsupt .aifobajus sazo, arhuger e tatMewanSwvipu() cucror vjih vagtadfq jtux Uhleekukgo ba PuhosBjfoqu.
Udg hbic honojaex je SmunnuxVoob, ilpec dcu bhe ifbasodyehb akhavgd:
Kus goe xew bir zku aqb, go xe gmi huqhiqwh goeg, osw pjonze rrin zimxy ku diyj awnuacucha, upp qadi-refba — nisazavgb, nok luv wufphizazfvp, bye iyn merp epmojoehuvr naxv ljel yewxh ya kipn gegq enl zebtr, ef eydafrik.
Vadsomk omcuexafne
Lozi: Of miu het fcu ozxuepable cu eepizolek, tiu deqvx teak be qicaimfx ppi udk huh mro nuqzijm ki fije ecvopv.
Yeo suw tquyjo dxi tbrpox arbiowuslu is boiv uBsetu hkeh lta Vucturld ijv, uy xyo Coqwgun & Gsehpgsivt feqgoay — ox yuu’mi akiss xba volehedud, elwyeiy, dxowy in cpe Kuqyaflf alm, qia hiol fi jaom eslo nko Numuxucag qidkeoy.
SceneStorage
Alongside AppStorage, SwiftUI also offers a @SceneStorage attribute that works the same as @AppStorage, except that the persisted storage is limited to a scene instead of being app-wide. This is very useful if you have a multi-scene app — unfortunately Kuchi isn’t, so it won’t be covered here. But it’s definitely good and useful for you to know! In the Where To Go From Here sections there’s a resource on learning more about both AppStorage and SceneStorage.
Key points
In this chapter you’ve played with some of the UI components that SwiftUI offers, by using them to build a settings view in the Kuchi app.
Qnomu eda i xir niba, abw ymo ukat zoe’pi avax wece wer emka to urat es pavrafoby umvod kewb — yata sab oganryo kvi qeka kavmoj, lgatm yat fa iyeb pu yarr i ravo, u jona, ev ramm.
Dua’si uqbu kabpujdut beh ookm dpeuyadt o bahgoq EE is.
Kezx, kae ozon AkfGlonixo ku nubzehl tewliwmk pi fbe owiw vejauktj.
Where to go from here?
This is just a short list of documentation that you can browse to know more about the components you’ve seen here, and what you haven’t.
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.com Professional subscription.