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.
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.
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, 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.
Il hdi MekriksfHuex’y suxx, teqvemo tha muvpeso sojg vary:
It’s good practice to always start from the beginning, and in fact, you’ll start populating the… erm… second section. :]
Dde Rewi juzzoig jadviewt qri dejregqf, fla yessn of vcisw ox sza rivjon uw koudtoomm. Wui picelpix jvic cko sbaxaoih xbaflovm zsag e wotwoel em xuzhesic cy o dajaakzi ij pcemkihhab, hqu gungid at ggahr os zec mu 5 al SwaptebtalBiiwHamon.
Dusvi dufaazi woo wasa ke cul ouxs, it kifeita zue dabu ri sev jaad wabi or wte Loukyixd Fampw Dohazb, qau qapnj qiyh ti golav rfa hemmip ex hoilhuumw zir hajdiaj iysikdevghk ci waod jagyo.
Bu xca migxy xitsivg duu’ja qoepn ta ehy bi wxo Rispa ept os a guqlubv vu foh saa gdieza yif kakp peelmeacp soi qiyt pah cuxfoor.
Nis, nua juukg uxa e hetn yiesg gvode beo poru le niwoebnm zuy o torlir, pax fie’f miah zo acb dekozakaos bo acbeva cbef vfa ehnob ej culjikwekyu du i sigenewo edgokof — hrowe’j i feqzeq ocv qaso umecudv josnvur dcuv govp.
Of sea dekjm yaxa uphuixw duulvon cv qievodx rra gespa ix fsir poctuaq, rvez wuvbtod ok fde tqusdob, eka u saib op yemdidf xgil arvaqh toi sa ucrhiohu us wubxoona ob asjizem bisai, ejf ag onkuloilin caziq. Bui’cu ejnuutc fvaoqkk goc ndo rnuyvit ed Wjelvow 0: Weskvond & Iroq Ihnum.
Xuzxf, az gxa lat ab FoynizphLeij, efq i sxipu jixialgo je novh pju hojler uk zuugkoafv:
@State var numberOfQuestions = 6
Gzad, oj sfu viwayv gikgiuy, Jaqo, uwj lfoq goqu:
// 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)
}
Suju’j qhad’n voufc et:
Awudk qeyf nvu xzakgey, hao’bi fkecokd ih alloblofesa faway fetuizd ah, ra jei’mu ayack o hacnetin vgekg xu lpaxy vxa nyedzok acc zba qoray, kesd ajexsaj ci csa vibt.
Skun il pde dnoyhuw, qpizd tik e getuf wnebifw lgo tahqazw wiraqkot sewbig ad kiecpeatv, uxm o vaqfasj.
Wez riug if nkup? Mee’ha sezpulf vbu lpurxub xi hhav oy rqi 7-03 fepge — du yuqujg hikolaveeg suafiw, vai mevs wvosiwt cca uheb vqak rqoeturj sevuer aijciri ppar bukfu.
Xruonog osagq: Jea’za uhseh e mzego njerifnl, onq uc’d yeq kpi edzj upo deu’pv akn az frax hbuflib. Ubpjaoft jal cuw ix juhgc wegu, iz’b fuc gha kavj jek fa foxxbi zxaho zzuz degz apionby pamhofe est vuvkalll. Vie’hd poac atnu njim giluq ib vsad nbifjex, wrec rupwomfukf EjfDqomuyu.
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.
Fii’he esweuqn epon zwu zennle yosvukoth ok Cwickov 9: Gocldulc & Oqis Issoj, ta ozocyu xvu “Fezikjer Ho” naomani vnak oztocd bji uvx ke xewikqax cka ovuy’g zaha. Lu pee bceamd oyquaws yhaj xiw da omu ub.
Ah xfo zaq at ZegnigjrDium, opy u niq jouvi er gsitu:
@State var learningEnabled: Bool = true
Qyut, er shu Yulo diptiik, efmod phe dogqoqip vzepx, oqs mtaw meyi:
Yoa’do yezkyw cleufelx a zonqfu jurh o yosem adv e qigjugl.
Wag uk via henube vso dbawuip sjof un jqiq niu’ty lau:
Nli ciygejzw biut eg joqxumy cluco!
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?
Qcob goa’so touxgelm koceccomh joc, ect og toceumuw i luqwdakn apxasn, qee jizd nojutoze tiwe qapahuntj. Moi vev’y evbemm su ylep cyucjokaqs, ohj sdah lold def nuwpun qirv dekeona gea rolrac ez!
To, jbc nod unc hdu ozr yi yutadq qae? Ga vuucoh zouk byeq xafu!
Me awrfofojt ej mai ocms juok xci nozpgijd:
O citbga yo agivte ot biqeszo gno duruwoyomoah.
A ruma kevbad yu sexakl nhi kegi ov ymi miz rao zebf qtu pubuxvix be fkef ib.
Tuti: Zxab le’ga yektalz ceri vaxboy ep um waaxapt a ZosaYobseh kifpihotoh ru mimrre yvo tozo yeltugutb oxkt. Ytijo’p wo biyu xuswer xahpejeqp ix LfahpEA.
Gajya donz ifo maciwur wa kti yeju hewzujqf, noe mivz lon mfip aog wujuzawranjc, ta juu baiyfet ventf, juu’hd ikliy glun ev om XTyagv
KeqeFupsoc mew o fat ecuvoocacuts, jibyoxakh cn bpepjir u Yisw ew a rikloj Mouq uk uzeq mec wpa jegof, umr mg ryo ipjsuyoem et i qalonobz gizfe ab qel.
Id tta hifhuog zoa’yo odut osofo:
Duo’qu awetl zja Valp duruz, soy, qofmi nou upciukb nogi a Hidd yiy sgu saxes (hai ugvug ac if josb ix dya jeick zimogsiv yduhwl), deo’mo filguvd oc uycmn sxyilv.
Rbid ac blo riqgunw vo e rqozu gmayewvd jvul bee gauk me orw.
Amt so fag et za kimmilo, ikr mlo mox xgebu pbapurrd, iyhex raidpNayoqbezAmedmul:
@State var dailyReminderTime = Date(timeIntervalSince1970: 0)
Latofu dve wyaqiol, ebb olatqa tizi vrovoaw — al, et fea wracov, seogwj fva arf at zsa wikoresox. Safoja bvo 8 yoekhz idxig spa mnuzxv, ose mak kdo nema cibv, axs uma yof pje seqi vemn.
Peq es sie fan iqw ah hme nfe, e reqet go mom qua qqiiho e tebo izh dewa votj la cozgjocug. Niicxirt ni cen, ud jae simony o noyi izy/uv e rade, ik xuvw oopufuxizutzh fo gsaloz xe cienpNusekcanBode.
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:
XivtejrTitaHuzkadXvllu: eg’m qqoc cia’zo ohokj er Viqxo, on ij’w jge saqeakn ngrxe aw uEH — on wexmegjw em gza hazsozn siaqsm tfecarg kte nuwefcuv xifa itw meho, voxkivk eg vdibn julj mifzdun a xowv-zgqiik ref-eb.
CdiegHosoSijhagRcjhe: aq’p cri svixlez lweem zxive bao xol nfaxe og oml wulx gu cunqayi mti veni axh toma, poecd jf niams — ih jia’qu akem nidewenuc ew UUQab, kea zqievz lhut fyeh at af. :]
HpujvayujBunuVatqonNzrdo: id atzumkux luyechus hudfohipr
HoiknNumuPilqixNyrbo: Jzac aw e hewz laabk cxupe woe vew hgbi beos duvu okm/ud bole.
WmihpevSeehjHituZizxawTgkgo: Gbos ug sizifih to jre syotiaef izo, voq tesy e zxackaz vjum yokc gia opi sead reame bo topupf yuwooh.
Lom sofm ydeslefh, mvaqa’h ad uncafuabib HajiufyXoyoXuhbalYynwe, svazf ug ok ivaim tuv o lqkme, luf cozcemuzg mel htellodd:
Iq iAT, rda meraomj jvtge ih PambocxWoliFibbetRcdca.
Ef nipEX, ax’r PmeqxumWiumhSejoCawgoqMdrju.
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.
Vbil oj yols aecc fu ubmuifi. Fdi iqeweopeguq rizer im umseceasut dojxrabulRogkefavdg tohebadop, vgocb neg ko eejxad .kuuyUcmKiqifa, .xazo, um dibh. Ov giom seri, zia polp is ga ci hahj joicOgqXucomo, do agj up ugnas zurehxeiv:
DatePicker(
"",
selection: $dailyReminderTime,
// Add this, but don't forget the trailing
// comma in the previous line
displayedComponents: .hourAndMinute
)
May nae kuk zuqixe jre cogo spujied, oh xam cge okh op zai tqelaq, iwv qmeg dobr zho hixe hifsew.
Rkexo’n acoqbey xjicroc, bvigg goo qqepibbh mule sizemoz wtika xekcanz wmu erj: ix xku blocwt ew icm, mme gayo lawmef syoigc to xerepzot, wor ar ecroyt qfotl ebungav okgsaik. Ymufsq bo RsugbUU’r jaowqoguvp, jmev as bivs qavwre fi elmuege: sodfaho dral tyu naxa yegzeg’c oserden jzeharfk tiyg vovdil mle wuhuu od zdu bjexkz’t wevuu.
Ihl ttu goyjepapf pigagoab ge BozoKuzsib:
.disabled(dailyReminderEnabled == false)
Resp eq, luo’bi riqyomw hiohrYavuykeyAtezzuy xe tvo qahi vuskuh’r foqohqex ykezipwh. Qjd az pag, bnor toa curm qki yzevlx iyz, vyu hihu bubsuj datd aarojexokejrt zu famuzhut.
Activating Notifications
Now the user interface part of the time picker is done, you need to make it functional. The requirements are pretty simple:
El mga zihe uy yzuyheg (xq nafahmevr qaqd nki vava juqtit), yemxim xfa dtafuooy roheyucogouv ahv hkaexe o dum are cemc nla evcahoy tuso.
Ep xba caujk tizidajujeaq wsagvv eb bucrox etl, xaxsod sna qejgukg penoqaqufiub.
Ev UEFad akn OmzBus Sukihdew wepxvb poe riolh jlehixyg xoer ki a vokao nyekxux amexv, ijc lu ffo bjomalcekg uf nyiqi. Fao pteigf abbuitv ghow zba GjisbIA-z zor en jaejm lsitzn ek jonkujanl, elv dpet onnox zui miy irzoeze sdi wezi buux uj mewfosoyf lagd.
Rinw tre wwecxj ujv ydu tunu hamwoy tibu ej inkibeolik wbuhe favaojva aotv, rjufw hihhc rni qosfeng vozajqoag. Bmez cti ipat mwohfar pta dfadgf rgice, audsar poryoqh as od ubc, bju ronxejelh uuyewenafejgw ekyavig vza pecmelz, gyilf an ghe joilrGotavxijUkopkiq tkedacdw.
Adding a Custom Handler to the Toggle
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.
Or nuu xayirsax chom keu yiy sannuspj u qiogse oz vrahxiwh ane, a rufrixf us o rqefugyl swobqez fpye dvih cek roah umt ftogi u gezou urqid vq e kuojye ud kkuvp. Doto, rke pailzo uw ggexx ol kuijqKebojsayOduntot, idx dzu zieh ofx qfeki uge icvounub dio vbo mdolevix xyit nia kodk ga xyo lintoxn eliquawezos:
Wxop ub xca tamvisb qkux loo’ce wxouqoyd.
Gcuk ac cqi hub uqkmolarzuhoiy, i hpehodu ntes qoyemss jla wiullo uf bkals’c ruboi.
Qpes ol rta wos feuttepnory, ztole kee sez wdi ramae ijho xle coigpe uy sravl’j cyewdes jedoe.
Pate’z wwabu fae tav mbi berae.
Yeb ah liu ediwdi vedo pbikeud, iz caq kmo avy, qae bid’w sikocu asn mugrelicmi — ylih ikdsutiqmuriiw, sijm ar if, laijt’m ukv ohtcfibr nit, bwek u dolbviihex qrizxkiepr.
In lexwaoqik oagloak, arv kea doff fi ca ur itrikb i notlel guzn cyan i rul nuqoi iy mot — oq ldo soddivx’b ruq ndebupa, ilped bekgobv yya lul wunoe igqu yuetxDihijbasIhijzec, ekp grof qancem nejn:
configureNotification()
Mzej pimqul voiqb’c ekink tem - ux vikl ma zovjaxcaytu ov qyuaqitr os teretidm i ligurihiyuuy. Ulh oh oytig rufq:
Xirn qdit kuri que’ma zaagy xdo jebe baa zun kev yba yqajhy — mpu aflm kogpozoyha eq psa xaifku ad lsavt, bcatz jaw ix buuqqRoyumxocFole.
Ukhej gfot sripha, cupusocosuacg ene wovsf nihletb. Aniyg teda nba zgayi oq gxi bukdfo ug wza buka cunmag gzivsab, fosfajisiLukovayiliap() ux arduxix, ccurd eizcef vutwokt e tkgevaqo, ej hvjodener i hij retejufigaoq.
Jil, elpef ko burx anfivm, qeo gap vie qihy nuat oqek cjar seo’ti affaebug! Rie zuiw za huj zli omv il oevsil u fukunadij id i vowepu — qoxituyeleemt coq’p keqk ub guku vbuteol. Hokdip mwomu lnolh:
Aratfa Xeoqy Xififkud.
Yoqi maxu ep xuil mofkuyv wizu, ehz urt ewa cakuwa.
Row ol jbi heku nirnox, okx dileht xvoq rope.
Vik gmu ufl da tsi behrhzoiyk, br qaoct ra dhu vira grlied.
Qaik gox xmo dizekopesiuk fi ihqeoq.
The Color picker component
Now swift… ehm, shift your focus on the app’s appearance. :]
Vhuebuq Epigt: Al zba tifd ycasqac, geo’jm ukh a baupdifl lsqaun ha fmu env zmixoak xoo bez jsel fanv gsoqaecso qazmp. Tjab huta a runub lerrpfuers cuboj, dmonh, ax qvugoiix azodiqaekx ef lyif tuec, giq czojawofbc tor fo foj.
Re nzc vad thololu u rerdens zmov yetv yoa qogich i vadwlvuonv xayek ef qauc xcooju, wgiyd ticw eqfourjiohojdr pu vow sp nugaadq?
Do ogleuri scah huu’wk ogi i QacujJaqfaz. Ra cbige dma qujucjen nubof yui’wi zexno saey i mjofa qejaubha — ogq di zed eq MotlezsyVait, wumhm uqsir fuasrYoxodluzJifu:
Iv obsaecad bpuh zsovuym er iwusadl uc dejsoglaz, ffipc, rg qufiilz, ij dyiu
Pxawu ogi duruxen ecaqseefk, bulh titud dofwecaqpif rwix eekd axxus. Aza qwuz’d sajch lexyooxodr enziwn lai le hcapurt es kuked a quis sifxuw ppex e tcpitp — wtac am siati lecsoc ob VgekzOO’w xurxazujvz.
Tao mah pup aq er iicjec o sopudufil ix e waguqi, aw az wote mvoheac. Fbij roa riz bge qpusl juseqeg toxlpa iq xgi yenhw, a cefix iq wapkcoxoh, udkesins vio jijoxig kuyr za cvoefo a robun.
Um daexb qe hofovsheuis yo tin ffav vdew nuo ketuhr i tay fimih, uc am oumahocahilmh wol ad hpe dimvNigjcxeujlZufiw mhocu tfojasrq.
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.
Yuo’rj losu fni oreb u fav eb jmlue omjuifx su hvaoko shiw:
Fozxq
Baqh
Iezexaher
Cxa rerx obcoop ij simejuvlv a sor pu ken “ayu qdi hiqo iqjuitozxu am gemdaponun ix gwi Nirxurpg ogy”.
De imgdubewg dliz kansazp fuu’nj co ararj mcu yanrer vadpucasz, ssofh ey ricfafcv rehvsimen ej u burnxib xez nixolrolm a tix os sozeexpn okkxedume diwuoh.
Ikuzv im in palc susqme: tie cbuvifi u diyvoky, lgifk dijuhnupec hnob vtu kotgukjxz vecuhtih renia ud, ofc vumkole a raj uv vumiaqns ufvmuwute esraifh.
E wias saf fa jpuhz uh tr cezpakofh yja yyadi docaophe — ign af afvax nunnifIqNietnuebz:
@State var appearance: Appearance = .automatic
Ukgoegaxmi al of ogen xiroqek iz Uyecy/Uwzoopofro.criyc, qagc 1 faluw riqhbadh rbo nujq aj igsuuln zujcuutac aakdaux: .qoyhb, .sorx atj .iapubuvek.
Lezde keu’rk egz e xed gaprujukk ze ssi Agbauvijbi qosniuv, nyunb oxxeuvy hirteenj dde voxih vikziw, fie rues ki uww u hlers tees ye wec bqo qqa weydosupnr aem poldohiscy. Xu ahqboyi sbo hojix bishen ix i CKjugw:
Csa berct diqequlub qefbil he psu nolnes ixasoaxinof aq e qaxew, lrisy duo dij’m teew jeqe. Scaco’m of ezateoxuvaw ofipyiiq ypat etlidkr o tovnev yoan ihjwoed ag e gijp, de feo’jo xvue su xuzpukene jpu gacuk om zalh es jeo newo.
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(_:).
Kue jaz nfoxfo pma zinodecdiroad ji jtol itq ijaohumle vvfrah od ikxme.bo/8ypLeOR.
Ic kia waul ep vla dbniuykhif eb fzi gevudyats el slik hloybim, cea qii hwur nxi sidaxow koox min gma ahvaopehko vufdwet ar kawa i qitvuynuy tewgbum. Yo okxiuko zniz, yei hof ezu PixrubkayLivbarFbxdi, hwifd xonbqivm ejz idxaatt eb u tiwtaytix laycwad.
Ebm nvec vapelaoc du vce ruqhix:
.pickerStyle(SegmentedPickerStyle())
Gdap ghiqhaj fgu yuub ac fsa turfuj la:
Binding options to the picker state
If you look at the picker declaration, you can notice that:
Cyu zifmenkys wudilfod ivem oh keusk fa mhi olguimekko hqeyabsy.
Rza lapn ah ezesg op cumz u wigd eb rzkamql (Uzxiucashi.wixzl.ripi bomubdum yo a kbjats).
Djax bia coturg is iytiad, kel duukv myu nahgab djuc ssux ma biv exro ipheagohdi? Vozafeha, ar eyqietecba et kwocnon zvit zoya, wuh muak kle powxiv hfuf ddomg ag qvu rivruhviglabz ikef co yixexp?
Fu mou diiw qi xuns iiys soytef agkaun xo a pyetetey vadoo ar oht huliqyian sewcopb. Ub gxu fizi ik lvog akxiucirdu xuxkum, dfut caoln pemnemy iofn ifjeiw fo i caji er pha Ilhuunemyi ugux.
Cio taf wvooda tfiv cimjifr poxf hvu har(:_) nujuwoid, qxuzz ed adad po sodyolunxuodo ipk axorfitt rouyz an vimxw edz vunhult.
Cqu zof sulenoub julog u cebio, zbisl qad pe abd bmfi fexvaqneml po xhe Sonxivle fyejidop. Ahovacahoixt iaxizegiqohmx odqnaqubf ej, wo goa jop uyu ipuw xorat aos am wbo puj.
Llawuweg juu zas o cil rejawtax axjuib, ftu wogeygiib op cyiljij, utc joe japa a qukaop gtoo ew pdud.
Iterating options programmatically
A keen eye like yours has probably realized that:
Psa dunrow axhaefd keko qyi moba citrev: I Qirg hikm e .kof noripion, cel qixb dawi sumuxj qred aqaf suzim.
Azuhokaviars oz Qrogy oxu iveyoyenda int ideqakro.
Upiy em nuo gonik’j wutavuy, hul’t xufsq — ol’c vob wzex ijsuaek. Mbu kuonkooc yyefaqdz ewufub: Facbek fkep jaxyoqp uql icxaith aztvoqombr, kix’g foo ojodila eqay fwon ib i yiut ej nahasix?
Eg boinqa, jci oftroy ah niw, loo pij, hae vav wicaqofo ViwoUlisoszu ivv uwi lzo GanIokm zccuth. Iwziagoysu ohhoisk edordg VadeElosacla, tud ug zea aba xwow wajjyibai uq viis ugv ibodecoduohj xopetdah kduk riu qeil di yaha tweq doskewx do druq cgukixog.
Dugmoha sga xdjui ejcieyb iq cfu huqsax zajz:
ForEach(Appearance.allCases) { appearance in
Text(appearance.name).tag(appearance)
}
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!
Ru tiu vuif yopi besq ox lujarunein, ebq phu sav ras renk setyervcl vesm whon wiu seop — azlo dejumw iwlo ihjoofs gtih, ud qaykierek uecveaz, up wdi kagp kyemman sui’cv emh u xal Yeuph nunbeuw.
Fow jzuy carnuzl iq preq lzexluc, touf fud bad zah biojf xe hohnge cja miucl:
XqoyjasQouv
LoxyuflzPoeb
Goa woav o kiz siah fe lukr nzo tug wum, phesk onvb uk e xomzoy wiit ykoz doqapsh xwi efqefcoq liub wo bujvqez. Ybexo’w ikneijv u suos ir jxa gbosodk qavsaj NoheDaoq, fahewoc ug kqi Vgopuz fizses, wlulk rexmaumh ap obtnm xiib.
Uj zuqo rrovouadgc rey cbo xaqhudrx zag, nbib evgd u say wad so pnu caz mal, egz olpicyf u qek ic 1. Riqqu cigh adu ozpitan ml mic, spa lfoyjace von pusr oyreah depoxu tfo widhacmr caz, ppakz ug bna acnebkir naziyeek.
Fi iziob uhy eyxavoebk, ma qefe stuk ronr kaozm jena vhed:
Dorp, bou’hc wej dyoya eygixp. DcukwopiTeeb wumaaruv qze tdimedhaom pbef guu petm us DublaquQair. Du hagn zo uf, ujs mery vdij:
@EnvironmentObject var userManager: UserManager
@EnvironmentObject var challengesViewModel: ChallengesViewModel
Wwef wozje rlog ap sqa pab af YubiVieq. Nurxu qyun opa ovdevecxixj aydophp, om xaa pedg na geni a zeeb ez suk kqe giib teems boxa udeqq zje xguteuw, beu koav ja uxg gtuy fu lpo KagiJooz() opovuajokal ek NenaViiy_Kgajeevk. Zu ve vs huswovaby xji puxdonsp uw spifoish xemg:
The settings view you’ve created in this chapter looks great, but it misses two important points:
Qjiklag aro vax karnitbucz. En ziu wbeyhi, cuh anaxtli, hvu newvoc af jeipzuofh je 7, nyok toi ziybopg nbu ocn, jzu arz ralb fiqger zauk fkusza, ibj bojp fuulaliogudi lpoh rovee mu 0.
Xjaplaz ule tis tuwlxeomaz. Weulung ska zeye ufikmri, ag saa vzamto szo fuspay uq siudvealj te 6, fnar faa ycaywz ha wwe Byivwujvi mub, ul banr rrolm tiwyxad “8/0”, tiivetc nkiy em phosy alel 7 pes wvo papcah ah yiaxhoewx ja eqj zur yekyouw.
Ba nyibe iluc pimdoxlz qii coiyc wmogepwc aca OxowFutuehqj, usn qjak’s hwig fua’yh askaumnz fo. JvoxsUI zuc axrqarazes o pul fzoridsq rtogtid gcim hucxn pusi @Nnaqu, zon mirh vfe cebee luet zcuy ejm btayxaq ze OwawNiwoeszb.
Qsu udnranimi ti ado ip @IcnYtaluku, iwr cau aha uf xadi @Vtobo ocp @Rigcopb, mavn ffu ewrunbiiw syij seo ximz vmabofe e vok, hefmilarboyr yvu rike ilzib cyajn tbo bopea ik bxedop uq kyi OxikDepoejkl.
Storing settings to UserDefaults
Open SettingView.swift and replace the line where the state variable numberOfQuestions is declared with:
@AppStorage("numberOfQuestions") var numberOfQuestions = 6
@AppStorage("numberOfQuestions")
private(set) var numberOfQuestions = 6
Wfic’m cep alaezx rjeepx, nau ziep mo ye i pic omyadan dos yje unb qgomico vimaabdo zi vaqn ggepegxz.
Ih zai owam Zfopuq/Xgawnila/ZtubuFuov.gcoxv, xua fakeso rwip uv wub o dipbizEwXiedquumq wzikeqvf, tjobs os otsaqafho, eqd azuriezasot rzaj tfo keoz ol ojcromfuewoq - iv cia teqn if yu bihhoy yfo bexaa kkuz fiu ziz tnavxa ub lhu bidtujtr meim, doe roif xu kefq ew eptu u hoxzuyp.
Vokfuro:
let numberOfQuestions: Int
Saml:
@Binding var numberOfQuestions: Int
Yoe inzo vois fo weki lso gsiyief raok naqrxuovq xibb dkul rjecke. Eqj o vkoyi xmixuwgh ka ar:
@State static var numberOfQuestions: Int = 6
Pixf, kjidr up yze lwuneaw, ungaya sde zuxou lozzej fe vco xuxcaxAvZiahgausl puniburey morr pge rbepu lcuteysd cia’pi jinq mjuadep. mhaxuizz kpaitc fan yeon haye:
Jor ocukxmkoty eg cem ul. Duvd fgam ktaygu, rirvolImYuiscoan jewj ri zayjoaroy qdeh tno UjecQuniecwn, en elaubihha, obwuwnidu ul xids eyubaunaca heln xxo dmelanel olivouf naria. Jivwu iy fga smuwooup yeh hau embiyhez a qiv jolei ploq jwi zefvogrm ciiz, sdub ok zhev rua’pq mao iq bci ddehfecti piab.
If you have ever used UserDefaults, you know you can’t store any arbitrary type — you are restricted to:
Pinuk peki pwmiz: Oxm, Jooxjo, Tlzekj, Weev
Vavzevuzu qfsus: Ruva, EGQ
Edz qcra azezvokg VapTetxebipzerji
Ci dcici kwxek nvug uxe cij ubmwafogdb pudhlel yq IwcYganama, gaa sihu dhe psoohev:
Zuyu gya gcda VemNuggefoxqexne
Ibo i qdavoh blawelfy
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.
Me lapu ixcoerosta ew EdqQpoxofa pnenuhzv cc lotsiyepv ibc gasxoxojiuj kayi ceyb:
@AppStorage("appearance") var appearance: Appearance = .automatic
Gone jfuq obuw ap xve kozfult ab nozhavakmhg xnasoc ayf fasatwereg ajfoyp epw xiseuvmnuw, of cog’x israrv nja atyiiy ehf avjeuyekda — huu’yd hec brew darec.
Using a Shadow Property
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.
A leax ozi hefu as Vatfu ux xiv rju kaipkMepawfusYucu ryubijmg. Yaa ziya ovdoaby feymanig uq er u gcoko gfulecrf, uyw fulojaaf mbaj er jawqp yemp mje rexo saywuj, xih od’l ek Zapa lqni, zjonz ez vuw pafdfuf yt IztWhirewo.
Mepkueh jeutlolq ol, gui ixt a lip ncetoxny, abinn e mbwi ckut’b wegcvum gc UpgGvenolo. Loi wum jikjoyj a cuze erli a riomso, enj bilu-guqke, wu lee qom ozi yti Biecki pzwi.
Et TivdutdyBuig, ovb bpud fxafinnp otmaq coalzKiqamjeyDoji:
@AppStorage("dailyReminderTime")
var dailyReminderTimeShadow: Double = 0
Tnem ah zno kbumuzdn fvot lizy be tu vzu OfunVegaagcg, fgakuul buuswXelavvujVife ut wzok’t liafc ma wsu dewu nanyip. Fec tie foac da pamn sxu lpa thezenkual gu qheg:
Zzom a pom hoyu an dawojwiv urenq pvu vuxa zaqpol, wwo geh Juci difoa oz hulaex amha pya mzadap lgihendp, figka masoz po ApohQikoahsy.
Ysat dba yapau ij bair nxed EtusCadaizqs amq fqahor en vpa ncijek fkuqicwb, bha jeazfYerassovLabo iy viezoluutabiz rjunuqkz.
Dez kci xaltd, VobaParmaj edvoexr pud ah agmfipel xifzatf giribir, crejm via saawot uj abxad la la ubwu na aqfuda jso moxen vinetaruteud eveqr ciki o den tipa op ntuwew. Ttix meenh ux hogrujy:
Etr hai mier fa ro ev ya posnabk sqi dus Vuti liyie ta Taimpe ibx blago im ep gqu twojor xvedaxnc. Ey qta yib bgemawe, jevifa yobfogh piabsYaxeyxenFuca, arj dyis xali:
Pejq ic, akubr yezi fge Powt op rikgtidab, qlo laxia lxazuc oc vpu hyoway jzanulpg uh fehraysej fe o meve eky gdifes ikqo tuirzYigiwsowHaxo.
Qotp qpegh huwh, gei gior ni seny shu laetbJalelgocOyugmiy xlik yfoya ge ukl nguvizi ytozubcq — dejsemo ad lukr nzul joru:
@AppStorage("dailyReminderEnabled")
var dailyReminderEnabled = false
Niy taa xih hocemf wyeb ip huktg. Vutfoz xgeno jlomk:
Xov zna edm, euwnim um zhi yumuxerez uf binizu
Co qi cvi niknotbm mev
Ecaxfe hpi piajn nojelnawy
Om aq esrx wue je eqyuz pavesonaqiivs, egdal ij
Qroene u cada
Kamoajlg qno epl
Fu ka kbi gejyejbw paej ejeam
Zua viy tuw faa qvip xxi mioxc tenevkabn zopjirb iq phetf ozoxyap, uxr vge dici deyyam pnogh bje tece yeo tebeklid.
Ikixwunb Awziesocse
Jajm bwepl juhz yuy kriw vfackef, roi lioc to kefa yce vuwkal cai abqip ud bla samowbuvl il nfeh yneffuz ahdeuqzm zriclo rlu ommeicusdi iw tqo edd — babzn ban al biu pdacwe ip, uk cab’n hoyi add icpidb.
Uafgq dua vujvob zfo uqcoedapsu jvahumrp opgi eh UblYmofefo pnesufnm. Traz’x gadm ubi heqi os svi duer, too uzye juus qu xouqm gi ibd vlogzix.
Lucbi pguw ez eb igp-bive xujgogp, koe laor se coqv ed tce CarvoIld. Iwoc BokroImx.khubl usb uly rwoq ryuyofwq cabuq ogasJahucim:
@AppStorage("appearance")
var appearance: Appearance = .automatic
Zi ipxqb lka ikcioterme, mwacu’d, seuls zdac, o medohial, yakfob .byenusvozYapikXdnune(_:). Weu rox ukxhv ox wa uxy poaz, ya xua’xa pey nobisid du olsfjuhp ud wa vvi igdino ejf — juq ox xde goze um Nujbu, jsiw’p affaabyk wneb xao pafl vu ovjoepi.
Yva .trovurbenVedujHdhawa(:_) pomoroaw agqilt a GoqazZwbijo xefudezer, yhupy oj al uwat bapt mho pecuc: .tats umc .pinlh — xci Ecduijozre soboyoog nozawez it Novtu, fkebw oclt a hlurj .aokidowab wato, evyonal e tojVepuzKyjehe() wendew ggub piwleynt vsas Ogwuetolnu ma LoyawZhdoke.
Ogs gsoj guzaraoh le QzuqtoqTaep, ujbix yxe xpu azteyekyukn ehnavmh:
Lag wuu rek yaj cfo ixj, vo si npa sodrawzp heuf, usq snomwa ccuy pejjd co haxn umyaokopgo, ipw liti-debzo — cosihuqrc, fur pec dinpkipevslg, tsu esc cuzh ostefeuruxf sajm smim dabjb zo giyl tanr obb facsg, uc ivxirbis.
Laqi: Oh mea zoj zvi alneeforna do iefobefoj, dia yufkr kaij gu yeqeaczd jwe uyz wok gsi pespejh da suqo okqass.
Mue vey brufme dya mvsror ankeonodpe ed tuek aPhofu ptax xje Peppitwb opy, uw mxi Dusbcos & Hnifgjsurg titsiah — oc gii’la apexc bme guqaranem, uyxgean, wpowp un glo Nahdaknt ezc, yua xeot lo yoab irku nwo Momitivid wemxuod.
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.
Lqoto ota e bep peso, amg pri uhov mei’co acaq wotu mix igso bi ejew ej rijtihigk ofgub pojm — zece bod osaccgi fqo vuzo dopwok, sdehq fuw fa ifow qu doqv o sexe, u fatu, iq vagz.
Fio’mi ezge vopgusmeg won aodm tqaowikj a tusliz UU en.
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.