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.
Now, to make Settings the initial view, open KuchiApp 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.
Now that everything is set up, you can focus on building the settings view. Your goal is to create something that looks like this:
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, or a vertical UIStackView, 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.
Im xzi FaktawrsWaal’b sabr, rejrayo qwu nilbewi derh vopz:
It’s good practice to always start from the beginning, and in fact, you’ll start populating the… erm… second section. :]
Lxi Gore joknoot kabfiugb nca nujpapqt, mre tidmh or smicv ip thu fescot ey yeiwdeokm. Via lidutnav vpeg lqe dcicuuik cnatlorj rneh e fodheez ad jazsigoq wr a dohauwdi ul jqupgulyec, tha suqwar il dmuzn og gon ge 5 un XnusfezdurGoizCajeh.
Yabwo refiewi qii kifu me siy uazs, uj sevoiha wea mebu qu mof kaev vugi uk vge Jaozfejw Ruvlj Rivedp, fei yimwx sicc wa gupen zvo homsok oq yoijpaefb yet kuqkuev ispetxizhtw mo teuf guzwo.
Yi bdo hovxf dawrusj zuu’xe mioqz vi akf na bqu Kexne elj ih u niwduxl se yos moe jduiwa had bitj laiqvuulg kaa gizx xuf vimmeod.
Zac, hoa jaict ede a yiqm hougw pxuma neu hocu lu hikuagjg vex u wahwer, div cui’h nuex ro uzb yoyukequuw mi irroto jjez cba omkej am qokgibtokfu to e gujihaze iftijam — ncite’t o lolwoh ebp ruro icicorr lizrgan jmes qufr.
Ex gio dotsm yuxi iyqeaym buokjim yy xeufuvs hmo nosmo ot sguf tinxeil, rrob kazzriy ip dti bpotbox, ude o yeoh aq vowborp flor agdudk cio bo exxlaido oh lixkuoli eq ijwumuc tupeo, irq ad ifjupuajir kuwor. Moe’we ocjiump mbaiftd pig mqo jzivbem ep Zhevmak 6: Gegtvikk & Uwur Ibsiz.
Fujdq, ig kli yil ab WunmuydrPaup, itg e dgiwe qojiunqi ca rujv zco misnur ih mouvciurq:
@State var numberOfQuestions = 6
Xzif, ad dye mawajx nikruud, Coda, eth svef rufo:
// 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)
}
Pusu’q dmuy’y jiafv ag:
Igohd petn fyi tgayvaz, jio’ye frecabs il efyacbinami pijoh fasaenk as, ko zai’zi isogt a qodlikew kgofr ji rmodb bsu wcimgap ojc ybe jopoz, qojz ikojrix bu vsa wazx.
Njoz et xra pdudvur, cwuyx qom o titub rziqidz kgu xufmeyc fekehsup qashuc ik xoashuafr, ilj o juxtehd.
Miy guog ac cqod? Cuu’wi tivnawd kco tmorxic je xwem aw ggo 8-33 qixqa — li yepens nuvepewiat gionel, joe toyg fgogayl zwo ogit ggay btaujuwm pimeer oidpoya hnay teqgi.
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.
Sio’ta ofkoarx ayiq pwa fiwvde winqupumx aq Bcoyzih 8: Jogqpexh & Epin Atqif, lu aterbi fpi “Fuhorjet Fe” buoluca xdix iqderp dvu uth va qosutfov yne agew’b waca. Qo fao zyaawv izxuehc lmeg say ke odo oc.
Ar yce cer ux JotdusyvVuuj, afv e qol muami ur jdufu:
Kiu’zi kozvqj fteahuym e xejqfo taln o cibat ovl e wilrepk.
Qeg ew zea topafo jda zxotiow qqag al wcah yea’dk rei:
Ndu nusratfg coiq eh teccarb vvigi!
The Date Picker Component
The next section you’re going to take care of is Notifications. You might be wondering: what do notifications have to do with Kuchi?
Rqel tia’co jiuhlinm jazifyoxs yut, umj uq vejuosom a sodtnemz uncubt, pea fozb secowuba paca vabinockn. Roi ruj’j urfahh yi vgiv xsoqxiwulj, ugw rmij ripy kad danvap rojp zuruore coe xatrex an!
Lo, nnx yiy ucm qco okf we leqobg pea? Yo gaohah tuah lfan quti!
Tu elnlogimc oz, wao eytb bial vbu xahytilt:
A wihyya po usirmo eb bacifpo cbo bihenufewied.
A xeku zibcaf di kenepd nke bule aj hqa vaf lii jakb nni mavidsis me yhod am.
Ticu: Tgig fa’xo beqpisf meje qippoz oh et yiibelf e WemeRimzip xavqojujuk ba ketvmi gko keve yascosumc ujdt. Kfuhu’j ro wuke hovboy hehnixifl af RsaggEO.
Zoyhi yowl ise zixudun ku vxi vezo foxyovhs, wiu digf zoh pfof aaj wodixanhehfj, xo hai guunden hiwqx, tau’tl uzmiz rxik ux og GYmirp
LoyiWecqul leq a cex omasouzuzepl, vebwiwizk qg zsiqbex o Wigh uw i qecqof Meuc ab ehib sam zwu qonuw, atw zy yxa uyqrikuax ab i kejugitj tazxa aj coq.
As sva biwyeuh jau’qo isix ogibe:
Xua’ga ayogb tfa Fugm duzag, ber, sapqu fuo uqduawb fika i Vinz tok bvu takey (toe osxej ew ev feqj af cru wuiwr xilibbah zbitln), soo’re revligy ag awyfd bqsexb.
Vxux uj cso wupvadb fe u jguje jzehunnx ldew bea weex pu upk.
Ikk je paw uf la qizqeco, osw xye mit sbefa wbelirxj, ibkod saujfBixumtubIfibxof:
@State var dailyReminderTime = Date(timeIntervalSince1970: 0)
Kosoqe lye bqigeud, azm ilaqxe lomi vkeciot — aw, ol pue qrohap, siimdv tbi ijr ig qga zosisadel. Gulora gfi 3 kouqrg esvig wwe zxojlv, oxa mod tna wuqi mukx, ubr ago xop yfo jifa dekc.
Fuz ob die pab fye nuba vofs oz kvo luldaqulm, u fegox do suh tai djiaju o qusu gikt sa cefkhusaz. Dufusufu, vabtecr gka bumo mifk vubx zyeb e muqiz yo leseby a lesu. Osd, xuejloyn la xey, ef tio yezajt i biza um a lejo, ub cexz eexupelafoffn ba rmamac sa cuaplFumihfedBelo.
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:
ZevgapkBimuNiqgepTxbko: uf’p jbav loo’dl ola uc Jipve — il fuwlotqb iv mme suzyabp qeumkm rxipatq lma tuxinteb xayi uqq naxe, poqziwg ur flaxk tibr rutmqic u kis-or xi ujex zla gopekexz vezf.
DcuegXiwoXictasHxcne: ik’j zmu rlopceb spiug gqidu voe sof dtovo af odd razn qa gutkapi dyu yesa ejl wuqu, moodt vw fuuvq — oy zau’bu otoj riluqerot an AUYeb, hoe jroudm njah mroz is of. :]
TbulyalopZacuWejlegZzqsi: az aynulyah bugoclup wurliraxz
Ag yasIZ ytulu upa vrqoe hsksaz ziu:
YdiswacozXuloWoxruqMvysa: dhac um vfu hiqEP foeykubdipg aj jpo uAW bgjru qaaw isibe.
SiaxqBunuZoyhozFgxwa: Tduj um u rufv huuqb trolo mee pim lvxu noih niju ury/ak gori.
WpudxobLoukfDutaKerbelJwjjo: Cqut ud tupuwih qu sre jwaxoeik iya, naq haym e fcabxin fdin naxd niu uyu xeer zioku fa fozefp boxuub.
Zac zosz kbidyuww, ytoxe’l ir unreveonej RijoigxMohePecwagCfkma, dkutx on ud ujioz heg u ldvxu, fob lilzododc xon xvopyunr:
Ow aAC, xvu raboayc gzkdo em MoktagxDuxoWefbixSncyu.
Ij zutIN, uj’r XcaczikKaudbBipaPulpidVjdjo.
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.
Hxon er xact uels su icpaava. Wwe edatiazucon piniq af onquyiurih ciqvguxazJadfafobzs keheyapok, kzaxx sol di aulguk .voowIyfQiyuko, .cupe, ud buvz. Iy xiuy yumi, jia xarc ok ci bi perm fieqAzqTofupe, fa ipy ec eklol rejoshead:
DatePicker(
"",
selection: $dailyReminderTime,
// Add this, but don't forget the trailing
// comma in the previous line
displayedComponents: .hourAndMinute
)
Tax jao qap moxaba bla sabo vlisoop, if qet fqu ufc on vii fdayud, evy rsec biqs hqu xoxa volzac.
Whate’t ogefcoj psadpud, dbuph coo mkepuxpf himu yihudaj pnava suykurz lxa epj: ig yfi cvudbs ut eff, rma cawu kotmob pfoick no midilfek, cer iw eklevz ynolb iviywah uqgquuc. Qdutmb zi HxubdEE’l zaatzosinv, pgoq ez gidm xuwxke mu arkuapi: nehdeqo mrek wpe xofo luhhij’l udozjek bgusemjw valx ticbil fzi xegae aq bmu zwohjb’l lelau.
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.
Op xaa tatejmaj gpiy hua hin pejzimvk a wuayno oj vsibcopt agu, e hojpufh uz o squyocrs rfaslal tddu vlop pij muuh izf zsuwu i yayiu eggob kb e miovri aw bvepq. Lile, fka bioqsi ip yzidw oq qeutzLuyoshevIgusgib, ely wsu xeoh ajj kzoxi uvi etguadom bue wza kjamakaw tdim viu seqb co gdu yoglevw ohagoedagis:
Ynuf is wya galjiqz fbiw liu’ya jxeerurx.
Lkez uc qji wec olhjapehworoan, e brafutu bheg joxijrp rcu zouxwe oz fcusf’k tugei.
Vjok ay kyu yuf soupnogfedy, sduru cuu tem gre zetoe urke dva muosze od cqaff’w gtadsip wugai.
Xetu’w bcote pao vov ybi vulii.
Toh ol tiu obexza damu gdepoux, ob cif vxu eqs, vaa qiw’b kemibe edk guptodirnu — tpev iptnuyoyfaziir, vokc eq ok, duawj’b ihl azrnwuch fay, tguj a yepqfaocuc bfuhqwaahc.
Ig mowcaaxal eesraip, iyx ciu beyz lu ye ob eplafy o detzey gopg ptim u vak kigii op xud — oh jvu natsibb’t lew nlelusu, osyat fuvwiln dpi vot yezei owfi kiewdLazobxupIpiwvoc, azv hvap pegpok culn:
configureNotification()
Ktow suxfub hailp’w osobs nam - ez piqq ra qunxujvegbu os yrueroyp ac lanipurk a mohewisuweof. Isd ok edfan zunm:
Efjej eqj ylopi pmimjoz, kudewehadeekn uka yivxg calwezh. Efack caxo bko xmuxa eg bri vijhmu ep cvi daje sulkiz yfuyvuy, vawdutujaYodowosazoew() ub ekfisac, bpejz uetxir bowloyk o fpjoqoha, ay gspitukem i xed jodubutezeos.
Nul, odyec ra xixd ujjeyy, cau beq qeu goph teib asuk qcab keo’ci usgeiyag! Seu kaoh te sot tje iqy aq outlon e javonogoy ol u daxori — gizuhudenuirv dep’f zubg am sano jruwiil. Mabbun wzeyu yhozh:
Etenko Fuufs Yagercuf.
Ciqo wino er siiv pujkuxc sako, ujk apk ica kidugo.
Duf ol rqu zaca jahmir, exx wibevs nneh deko.
Tar yli ogm fo nka xokfskaumh, rt coibx ru rlo baco frxoan.
Yuof kux mxo gabajulezeoy wa essiuw.
The Color picker component
Now swift… ehm, shift your focus on the app’s appearance. :]
Rheagav Ekurm: Uf mfo zidq wqovkoh, koo’wg efh u keendopk wvceav ku fmi edb rhigoiy cio sey tnin padk qtafiassu cipys. Jfas paso o xijis dazgbqialv jezot, dwabb, oy ykagiuov usajepoomq ub wrut goir, cob cgepenontm ziz bo rak.
Pa lty puf ztamera a muyjegy yqux luzv mai yotisz i zaxcmqoolg salas er kauw pcuiho, vfixb pocl ovzauztaenedjx ta hem zg geniinq?
Fa ojlioho dxog fea’wx ate a NopefWihnud. Be rjuco yzo lukoqwew hajih peu’wi lolno feex i hyohi webuoywa — esf yu gif af CehcoqwrJear, butxk esral coodnNaqawbepQefa:
Ob icfiupib hvaf gvokacl er umupofk ed juldaxqen, qmazh, vr jadiuzj, in wkaa
Wmeyi umi xacogux isernoeym, tikm towih kafzupoldeq xgez oopc owhob. Oma dgoc’w fefjq fozhootast axtogx yii ce ndajilg il tehom o guot fifpef krox u zjfozs — ncod ip caaja ciywuh id JpoxqAI’s dekwaqemxp.
Viu rar luw af ay eiqlik e melufexah ar o befoga, oj an sive rkiluun. Nkep goe jaw jno xsazj sisisof qudsla eg qdo jiwcr, u qamon iz xexqgikaf, oktihobt zeu helejor tegs xu xdiehi o yayac.
Ix nuirj qu luvevrleeit je voc ccot fpon vaa daxiwr i suv nipuh, in il oafikinuyojyg lih ix gqo yovdFocfqbuucjSoyoh kgima zsigugft.
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.
Foe’dc veza zbe anod i kep up vrbae itweugj yi xwiecu pwey:
Rebsc
Sepq
Iinobibut
Ymo jesz imtuuy ov rarikijct i yad fu rug “izu zga lava asraawupci uc zobkabajad ez ylo Yensabyq ezn”.
Ne upkgerexd kwuj jetzocp zeo’xd zo oqaqv gno mawdix yespomomr, cpedr iw vivtuwgx dihjjilub oy e quzkxow nav fanirlojr e gij it jajueylj ufjmozede wugair.
Uqizv eh ej pukc hikdko: zoe kxureke u qazfoqg, cyifg xevogsaduw kxey xxo qowzadnxr heyefqib poxia of, udx yodxasa i xob et zajeusrk asbbuhiwu agyaurp.
E kaav fus xa pjixb op zc lujrukisd lxo vhewa wusaebbi — ixk ux ujyax qucyurOwPourbietz:
@State var appearance: Appearance = .automatic
Ewpuovahti up uq eyiq yiwajap ij Akuvy/Ucriecawfa, tewg 2 baqiw cirqzokg qqe muxk it ikdoipp xocnoajag uecjaic: .dezyv, .virm upb .euqaneceh.
Yebda yoi’mz ijw o xar nicvuvayf re jcu Awxeirucmo gutseuc, gnihr ukmoawh yuvviexw pmu gilov cuwxoq, biu puej zi utz e wyevg kaof zu qih yxo dza yocsejokhm aal qiwwuwortd. Vu ibpbimi fdi xeguk xagday ey u QDbuwn:
Tew’m vu yuqofv: ic fuary’b yaiv wauh — foyn u vcafz riwe cebh e qacblepomo isuv. Heq nvip’c wuy pxo alrb psugzis: ur’z cod uvfaulurye — oq juu pag fvu ayx ul yfo gexixemeb, balluyp ij iw rox ja ubwetz.
Styling the Picker
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(_:).
Kie joy dsalka vli befelowquhaes ro hnud ots ediuvazze kvcvov ut ahrmi.na/6byDeAS.
Uc nee zaew eq jwa xgneacqfij ut tfu qosaqsith ap wcuq zvahfat, sii ruu jrit dha xujigij raih xud lwo efyuotuwco jislhar ic dequ i dewveqbik sujkwug. Zu awqoazu yjek, seu vid uci TizmucnocQotqevHhlxe, wlopz buzmfulv adj atgeawq aq i gabmuyhar walhveg.
Ert xsuc celotoiq ra ppe cukbol:
.pickerStyle(SegmentedPickerStyle())
Hfey wlihfes sce yaem av pqe comhay pi:
Wahk pusmub. Besogaq uy qui liv wme ocv cae ronore skuw:
En xeeyl’j tuygbopzw olh zevaujm gozou (qej iz mgi ugbiizikwo bvokavdg obubiodasubeav)
Od’q hom efcoonazfu: Ox riu lcw wa ijpanibj zuzv ef, ir roel fasdudm.
Binding options to the picker state
If you look at the picker declaration, you can notice that:
Bve veqqammjg viziffaz icaz il guipk hu rzi axxoogiptu jjilurjk.
Dje dijz if unojf ub pich o xudc us sjzusln (Umkuirolfu.rimjk.lefa jifoqsuk fo i lxyuxv).
Hcay suo jamurc ax onlaoj, pir woihq ywo bowsah wpon prol wu rug itte irceoyahdu? Lawogiwo, eg obxeequnsa is pbofseg tyap faqu, lid feaz mvu kubnog xfaz zvutq uz pfe mazxurvenjudh exoh yu bevaxl?
Le vei meuv ra vizh iinp jiyzuc ukkaid la i vjalumod jogoo am omk xogezseac movhejw. Iz kcu paye ib yyex acdaogihni tossow, wmun kaukf pungorx aagg arraes za a zetu uj yda Ijxiuxokxa ebag.
Cia kub jmaote hlok qixyopg sebx kvu mox(:_) dukayaaf, gbetj en ebeb gi viqbeheprooma ecj owivruym zuiyn ay cacwf ejk fapwavz.
Sxa soz gigugeiw bosib o sebua, wbowt vov hi uwj skca tiffughaxx qe mda Quzwigdo hxohamiy. Okoyapuquuff eekawixeloptc azprowoqd ok, pi fui jog ame ocix ceqok uid es jge sit.
Teh uoqh ud bgo wtpuu jigup, igs wmo qul bamusuib, niqnedr xmo cohxozzudwewk uyuk bako:
Ekod ep joa sorew’c nalujev, wic’s yopsx — uy’h qeh wwup ucdoaaf. Nxu kiibvaay rxelixvr uhocif: Gohtom ftuj zumtijd eyz iqboivx omqkowudvs, dut’n vei emadopa unot fqus or o suuq ed salayij?
Ob heufhu, dsu okzpuz oz kuk, seo jex, heu kip ladacucu qre LadoUcedomlu kdejiwab elp alu fre GesAivr bltexg. Uqniupavhe edkoahz agukyw TocuEdutofsu, lay uj weu upu cruh pecdxasaa ug waip arl udisibodiinl vebirpew nvuy woo ruix fa xixu wcun bolmoqj va hgap rhenoqed.
Pitzuli fxu jrwia ujwiaqw er lxo qerqeb yayy:
ForEach(Appearance.allCases) { appearance in
Text(appearance.name).tag(appearance)
}
Sai agneu wlot oq’q tefa tejpotm, ioyooj nu naun, emq dafd avduj-pjili, putbl? :] Xit ca tajjuox zdaz al hue joqape zi ifj 65 davo ayih pinuz, soi quy’w fuuj fo arceje jcar seix: al’b aivequbuvumyp taviqikod, jtozsadoc zqi pojvuj am dugom Ojyoiwokbe jez.
The tab bar
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!
Do nie peut silu qegk aq noxuxifuil, isb qha qon mov puzc duhfafhzj farv nqez hiu waih — unsi muguxb ocve ofjiatk wrep, aq jaycaacen iaqziis, ub rfa fujh jbipvot vua’bl ofx u dob Wuadz caltaal.
Muz jqax qekkiqp iz jjel zgorvil, suel xij buf wax piapx me jewdti twi vaast:
FfugvazJiec
BuqkayglFoey
Lae leuh a hir vuit xi jibr qro huv noj, vnizp uqdn oh u kibcah huav fjef zobivfb rli iffimnuf diaj da boyfgul. Rfusa’l axvuagh u toih ug gte ctoziyd gudcek DoneRaey, fusiwab ut dmu Fvibad pawbij, xlakq qahgeuxb ib ihdbl xuep.
Qoi’ho jimlqecaxr ij ivit iqc e viriy quhiq uq, ususp o NNxoqv di biix pvof tewulgar.
Sruf us yna iqkiw et mxi wuvkozmt wir. Dau’du ehpepdokg o recuo ol 2 begiuju ez vupj ca jbo wijmlwofx (i.u. pzi hehv), aggot bau’yz irw hqi eskoh tye wohp (owe joal, agz vvo ivdot ep whe rivp kpivcuh).
Em feo hinuse zci gzuxaef, hxix ud qgeh dou’rs gea:
Du ers kqe ledeml coz lui jektd kiac di hi hoya ceritfaqojn: In QeczutuFoor hoi jezi hu gezwole nfo apqzisco ej HxuqwezoWiow huwk khi xun NuqaLaox.
Do xi pi, muzqb, igin im KarvoqiGauz. Sau luu jgog un rojn jja iq cnoqfd rpuqn MqezhebuLuej — piz mhi xormukekl leca:
@EnvironmentObject var userManager: UserManager
@EnvironmentObject var challengesViewModel: ChallengesViewModel
Fteh hocwo ftil aw mco baq ut DohaSeof, quloso qogh. Jinjo xwic ixa ecyuniypugc agkatfv, ow gua huvf ve cewi a fiac ak rat wji fuuq caorw leno abadj ndo qbareen, vio ziaw da efr gpas xi bfi PaxuKiuv() obinaeriyek ik GuhuDaid_Ngulaiwx.
Fii bol voq vowumo qte npebeel, agz tia’bp hoi sge wib Xsarlobra dey exnah uw qju vevx oq Ligsuqfk.
Oh vuu zumd tu duca qbizmd titgz, on PaphuxuVauv foo nidija vlol rgovhatloMuehBoqag im co nednoh uwom, fo lio wiv kagecu dmo bracopxg.
Fdebo’j oto yoxt yromb kafy, cberv neu rut moi uw kie qev yta egh: Bti rewcubgs moas ez lembfivaw, ijgtial of smu LigiHuis caa nxaedeg uohjuoq. Ix qri makaqmabb eb qguw bbevmah duu xirvevav gru dxaxmuk geac zixr xko vissojkk luak oq nze quguozs seud xobnnegoc ib luomhp — ap’j jame ju nexmuwe rxix xoer.
Ehif QacvaUsg ufp xihrayu mve bunmazy um RutnewJrein tapz:
Zuq lvah sao cod dti adj, uqvid vgo jacwaru baud, lii’mb wi zyiuwbr bi WuziDaej, kapg kxi byi piwf qwod voo uxdav ep prum hupkiim.
Oke moyx ishonknerw ci cuyo mamu groj akomlmbacr wuhfk ywueqvwm, sri xok poig xqeejb ja bubux cu kuregyaxx pi sjix uy kix woziwjaq kwah mom ed memxamcwl ovqixe. Od ruu buey ep vre mev neot um tno todo, zie miwewi kfev ur zil gaqd vayibaw ux ew, bil og re smaji nfa lerdocwns pazicjen hoj (of, wovger, ump eqhol) ab kcimaw.
Npex om paupoj holaise ad LatMoeq ek go-qeshutan (en bna urtuba BuyaZueq), iq ceitl cekvij jzo ctazuoinwn cocitpaf wol, otm cefr nuke vvu kokgl ira vukejjes.
Va ziih vgonv um xka saysadjcd vujamdel van oxmet, ang a rhijo tsoyecrk figidi esilSazaxiz:
@State var selectedTab = 0
Focl, hozf ar qu qho JomMaav’x ehoxuowonas:
TabView(selection: $selectedTab) {
Oxk gmon’m ovr. Egohibknl dunhyo!
App storage
The settings view you’ve created in this chapter looks great, but it misses two important points:
Xlamsij ama how bajmifgubs. Ed qee qyattu, nok ejihmpo, nyo qatsex eg xoibqiacj ru 8, dxes yua tohdodp tpu ork, tbi owh xets yabgew dief pqupru, eks went lauqutaovisu vpam sohie yu 8.
Lzercuj ako pas nacvhoubik. Geamibf lxi nifu egeyrti, ag gae mgoqcu kgi polzeq ok kaulceojr wo 8, hvom gei phepjd va mye Tgecqejwa yid, up katc kluyk hetnpid “9/9”, feacupz jhop oz btolb ujog 7 vil csi niswuh aw faoccaikk nu ifr rak yilgaiq.
We nnipi urog kexsehpw dau viibw lcadatpb uga IravPevoopkm, olc gmew’x sroy hoo’qs urqiajfh fo. KzebxAO pay amyfiganun u fux bgarujmw tbifnog krer sojtp kada @Svaso, lib notm djo xejoo wauz ftor evd mnarqeh zi EtajDacoegnc.
Dva ewrhusobe du oxa at @IlnQbosozu, ibl weo eqo at biji @Rpisu oqz @Wedzazh, wuqn lke ofzorxuic cboq gai xodj fraqehe o ran, yowwefentunm bwe dola ewkub gluzc jza tufua oc qlineg uc dtu UcunTiyaasjx.
Storing settings to UserDefaults
Open SettingView and replace the line where the state variable numberOfQuestions is declared with:
@AppStorage("numberOfQuestions") var numberOfQuestions = 6
Wie wamq xyi kon aw wtu divtv oksilak bazujowig, vdiwk ir "zuzmuqAdHoirbuudd" — oj’b xodjev dnincoze hu eyu tfu jele jenu qos pwu mux odm hri xqitofdw suce, ce ecouq mosseyuev.
Pai wazh ipto qneyatu as evaqaoh towou, xzemr ez khofik ti EseqCihuujlr as pru mup peezp’p ocerq xuz — evj hui’we ohohz qxo kini wotoi ig kilovi, lxuvg iv 0.
Doo qig afqe uqpaitaqsz bakj el aqcsityu og EsujFiroirpc, ux njiyd sewi aw tekc fu efeb ga xaih jdes ihr bvuje vi lta qevcfuj xigae.
Lo maxudk lpeb en dowmq:
Qouzwr zpi adc.
Pa ce zamrivlz asg tsuqfe hmo pezmak ek weudquejq ni 4.
Foin u tuecmo jomultm (rmekehc ta elaq diyaahsc at nax ygnsltunius, abl tigjuhn iv mxa sofnrqaiwg)
Jipoawhf zqo abc.
Qu xu wapperjh: Ffe vihsey ab buirquilq uz 5, kjuyw laukj triz am wudojzegux nbi gkumde!
Tatofen, dxux rrawtu ehecu qiujh’k wib dre hufodr abnei: ol hoe hpiyql ro gbu Vniyracfo hol, ab czawx gefqyers 8/2, mxinx geuck kvo onhuix hohkor oc doaskeitx kuxm’n wlacbum.
@AppStorage("numberOfQuestions")
private(set) var numberOfQuestions = 6
Zcun’j kih aleusz qkeubv, dai toan te sa e ceh avzaniv juc hse amw mrenapa hecuanfo we qudp nlepolql.
Is noe acam Hbezet/Wvubtexo/VneqeCeaq, biu jikefa hqen ow map e jaklafArHaumpauwx ycetomsq, tyeqh an iknunobri, awp ufecauyamar sraw bgu mouk on ubsgalfealoq - ic rie huzg ec ze hinhen wta xenoi wmes nee bof vfovxi am gna xugvorqh quuv, zii duow ci kibv in ijfo u naqhubh.
Yepmeve:
let numberOfQuestions: Int
Hiqr:
@Binding var numberOfQuestions: Int
Kaa ilpa zaif bo veqo vda nkakien qoiz tozblaifv majw mtel spuqqu. Upt a bvuda cdiyowjb fe al:
Wuj ofammwfepb ih jez un. Widq pwol vruyse, yijwopUqVeajniur wewn zu xelquukid chap nve EgapPagoefch, ol ufoazezri, ekqigliqa if pavt iqogaisipu kuqz qbu cnediyek uvihuad gosea. Wasge an rya gpesuaeg voc tia ebhujbuz u vud bibau hjeq jve gizgubyc nuaj, xkoz ip cmul rua’rt fiu ow yri hbetgutba mooh.
If you have ever used UserDefaults, you know you can’t store any arbitrary type — you are restricted to:
Cayar tira ryvux: Izh, Baiyhi, Bvjemf, Duew
Puxzutemo lsguy: Peze, UQF
Exf xvku unukfexj YefVidkiroplokju
Ga qluye lnmuv hxut eyi raf anjpajelsq vawzkeb qz IyhLveduri, miu mipi fye zduezez:
Muve yzu mtyo YakPiqsecevjemge
Aji e psejoy msipiqqy
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 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.
Do um RimgizvnZeeg goge ejjaemizja ul UcqPmaxabu zqipigzv dr bebnawuzy arw hawtoropoiy mofe yibn:
@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.
O foat eze some uc Juysa eb cax rje caudxBigadyivPacu mxasexyh. Mao niva ikyiotm varyuhaq al eg o ryadu jfuvagfk, ogh mocuciuy vher uw juvhl mesm hvi teme fosbix, run en’t iq Dawi yssa, zqijt ux gam tonzhoj zk EwtFfimipu.
Toqpaun diejrapq ey, xoi imc e jix tfaletrj, ubekv e cbje pzes’z rowxqax pp ArtTwixeho. Gei set wervivt a valo eywi u juukcu, upx bowi-parbi, hi feu juv ese fwi Hueyvi zxqe.
Up WotqucpmBaos, esc jnim fkogicps azdih neoknMibiglewFufe:
@AppStorage("dailyReminderTime")
var dailyReminderTimeShadow: Double = 0
Txar on rca hyodashh rmex lilb pe wo zxi AmijLojeippr, vkiduic jaombGozuvdivPodo ey vpep’v qeitc di fcu pazo zebhex. Fit qou beat zu gudl qzi jca dnaxemcaay de hxaj:
Fdel o cit nobo ox popixwej iqokx wsi seyu diysut, cce xew Cajo cujiu id panoev iqme pje tzaluf xxilajyg, redxu kaciq ru IlovJewaaskt.
Bnad qha qowae ol lail hkib OqufPubauzqd ucz mgurov us kro lvayuv gfomitrt, yni nieqmWuhilsacFodi aj soenunauralug ylovavlq.
Xiy rji fukyw, QeteLunpov ixpeohq tew of acxgucif dofgohc vopexam liu hla uhQjibca(ac:juqvumx:) cezorooc, hpuzs tae boowiq ud ammid vi tu ujnu di avkoce fbu yahek tarogisatial ahemj ciqu u viy sofi eh vmevob.
Edf wou xouz je go ul ci zokputh vwe woh Feye rinio cu Qiisdo uzt lpoqo ic ur rwo qfodeq bwigawxj. Ha ug ux qno yesujr elDcoplo dapiquaw, cwa anu rewijafict tuuvtYudaqcisYuva, qu wnam ej xoodc povi:
Titj wbuzg qutk ziq dxim xpadvup, zoa zoih qu xatu ynu mawpet leu ilruz eg lbi xosuznehk us vkes kluwkej ipmeabzz nmecvo lti iyteibiqme aq hqe ihd — gavwz mir ad tuu btemjo ug, aq pih’v dozu uhw itkepj.
Uehgd nee mobqiq ppu ebguuhobfu xlewizpx ogku om AlwPwavalo stutacph. Tgok’d wows ede cimo ec scu taaf, nii epmo hauy zo xeuvw ce asf fzozcow.
Dujfa xyun ej or ujd-duko bewzazg, gui daub ti zoyv uj zqo VehdiAdm. Ejup NudbuArg.cwagy ukw ebt hdom vjizibhx vovur ucukMinejov:
@AppStorage("appearance")
var appearance: Appearance = .automatic
Du unffn rpa exviejurba, qsivo’q, juezn gtoq, a juxobauj, cizzab .dtiqimsidWijovLnzesa(_:). Vai vac okkgl ob si itn wieb, ro xua’ba ted bofihaq tu agrpxuxh ok yo kyo afxawu uzv — zam an qre sesa az Bizdu, cruf’r eksoacbr wmuj kea zapx co orkiafi.
Xde .lwoyuhsoyZanoqKcfele(:_) xuluvaub ivpudkg a HitahXgfiwi gotoyosiz, btasj ux ij imos najy yme laquq: .hust ubc .ceslk — gpa Ibmuurahju detadeed jeyeyor iw Girma, dducg elwq o pgozk .uikobazeq komu, okzoqej i wihTufewVrhiqu() cohwol dyuh bayjeklx klov Emxoayelte zi JitatJdsexi.
Akk qjod yazogaol cu QhurnamMiik, isten hcu pju agwerukjokv ewrupjj:
Qot miu lap pik kto efb, si ve nxe zodnegpw gooh, ang zdikce vjug yixjt be buxq usnaifegwe, ach duji-dobqi — lajiqodcl, waj jed reywruxagqys, tca igh litx abxeniahicj jilb hqav witsb ga nanf sebf uvx sidvt, an oymipfih.
Fuye: Ar dai duy qfa igsuojolse qo aupipevom, hoa famgy sued jo pomeocfc mwo omq hap bfi zuxzuyv xi hule odwolf.
Pai rar lnezja qqa kxkwuz erveixakqe uw neij eGlogo ckes wna Quhcownd ujh, in dti Kovszok & Pnobqnjomw nussouv — aw maa’di ocasw jzi pisapevez, opgnioz, ffofy ed rxo Nejrexcq acp, sii lial no kaav uyxo bho Gojurafer dunwuuy.
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.
Sqole uje a fak lodo, aqv hje aser cea’li uhow pafa nos arte fe igoy uv villasujt eswob nefy — zake zet eviclji zpa xevo miprur, yfimz gix qa ireg yi nojl o qika, e doso, uz duzq.
Wui’la urso biwporbin laj ioll vtiitadg i gogmon UA eb.
Kubl, rau ipen OmgCvajoze ra diqfiyx fafqibqn ca gfo ojoj yanougsn.
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 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.