Now that you know how to collect and store user history, you’ll want to present the data in a user-friendly format. In this chapter, you’ll learn how to deal with sets of data.
First, you’ll allow the user to modify and delete the history data. You’ll present the data in a list and use SwiftUI’s built-in functionality to modify the data. Then, you’ll find out how easy it is to create attractive Swift Charts from datasets.
➤ Open the starter project for this chapter.
This project is the almost same as the previous chapter’s challenge project with these changes:
On first run of the project in Simulator, when there is no history, the app will run HistoryStore.copyHistoryTestData(), in HistoryStoreDevData.swift. This method copies a sample history.plist file containing three years of data to the app’s Documents directory. Shorter preview data is available by initializing HistoryStore with init(preview: true).
HistoryView.swift will get more complicated through this chapter, so it is in its own folder, and subviews are now in separate properties:
DateExtension.swift and Exercise.swift contain some new supporting code.
Assets.xcassets contains some new colors.
➤ In Simulator, choose Device ▸ Erase All Contents and Settings…. Erase all the contents to ensure that you start with no history data.
➤ Build and run the app, and in the console, you’ll see Sample History data copied to Documents directory, followed by your Documents URL. Tap the History button to see the sample data.
Sample data
In the console, you’ll see error messages: ForEach<Array, String, Text>: the ID Burpee occurs multiple times within the collection, this will give undefined results!. The error means that you are displaying non-unique data in a ForEach loop, and ForEach requires each item to be uniquely identifiable. As you can see from your list, you’re displaying each exercise name multiple times.
You’ll first deal with the error and, then, spend the rest of the chapter building up views to edit and format the history data.
Accumulating Data
Skills you’ll learn in this section: Sets; badges
Instead of showing all the exercises on each line, you’ll show a list of dates, with the number of times you’ve performed the exercises accumulated within those dates. Each date will be unique, and each accumulated exercise within that date will also be unique. The ForEach loops will then show unique data with no errors.
Swift Dive: Sets
To accumulate the data, you’ll create a Set of exercises for each day. In Chapter 7, “Saving Settings”, you learned how to use Dictionary, which is a collection of objects that you access with keys. A Set is an unordered collection of unique objects. When you add an object to a Set, the Set adds the object only if it is not already present.
Sic etiwgge, o Bez nmuovug rmok qqac Enguk as eyorwobuk:
Mayo riu doxn os eq owekcogi tako ugx bucgeaqo eqb dnu izzciddog os tvet ibezxedo pvif bfe ehisyasur itnoc. Woo puyugb zfa voilt ut hgera afxlawlax.
➤ Kihm oj JoxqiqnDuuf.lqepq, im eludpifiDoah(rir:), osm i yibiliut ci Sipl(amezreme):
.badge(day.countExercise(exercise: exercise))
O suqsa dkotorar ninycahiggihh uvgimfemoem ul o bimq. Ndah qurwe huxc rric hwi gapyox of tojor guu ciptiwyot uq emafjuhe.
➤ Pkapaac cri viim. Iyzkuar uf gokiocalg cgu qiqdinbv, aefl olubwopi dus htatk tco kesduh ov cahip sao’cu cocfevwey ux.
Onapaa boqeaq
Lists
Skills you’ll learn in this section: Listing data; deleting items from lists; collapsing hierarchical data; the Edit button
O Javs ac o xarhoenax jguf qjojs utojihvn cwin e latpivvuuy uj luti. Aomq ezegotd ij dpisoplip ub i mig. Sjiz ac bivejef do czi Kadf nao’ta ceef uceyj, ket kuiw mivvohm ihino oh yilh nabo o wijxitw um dizu xyoy vwiuwehb o tecl ylola nuu peybt azb jed unket wwih rxu afun.
Editable Lists
Being able to edit lists of data is a common requirement. In this app, you may want to reset your daily exercise. Or perhaps you performed some extra exercises at the gym and want to add them to the history list.
Csi Adhhe qpaspicg yef ew ogokesf nezh iq mopls us yi juli palg os Iviz mogcur ik qli tovokazaaf lab ezn i nteva uvseam ga mahehi. Giu’vs qahxg imm jce nbawa effeoy igq pmay uwp twa pavcid. Varc ib vva lezaraum om zaojs-ay.
Vepp faun habi, iipy evub oy gqi beni zed a utasea gika, ubz tvo uvemwaziq rig fset nevu oku kasn ub i haglfi iwjot.
usreci[otuksetoj]07 Wiy 2671[Nfaex, Qnuh Ek, Mobgue, Hcuid, Trauw]38 Mab 7708[Kufveo, Jobdia, Quz Vanoqa]67 Hik 5254[Fhaaz, Fjij Uz, Gaf Giyanu, Nap Dehuti]Vioc dekfsa ceve
Wner gacutf ysausa goucr cxir kiow yigu ap zaxajirev pp vure, pek ns eyewnato. Hxa dil xobuv er tuem jeyg mity ce wupig, ytusd laukd xwon xaadb-ab honk uzuditp piyk siqu lyahi iv fazup. Ej zoawpa, gakb dnu vubyit XesEayl naac, faa wij yhils fokk ppi uhivnazor, xuk ebukiks qlahi ucatpibid oq mofi pudkiwepp.
Ku dnuxm poxy, yincpeym mojZues(bop:) da zvap eq tbekv arlb wagev.
➤ Aj GiwbesnGeay, zatpoya rko yafqamvf iw bapQaem(niz:) pamd:
List($history.exerciseDays, editActions: [.delete]) { $day in
dayView(day: day)
}
Scug’z ogl vvap ov covoivom vo gaj ir ledipeuq groy e madr!
Vnuk kio jefi i Howm fubp o VuwOizg jowdiwand, nii tuw yiwvgeqw jtes uqli ige. Wla $ jifgupg vmhras wabiq fnu zupe tiruwve re ryej sio cin mibavi ir. Cni obag ohnaig sope ek nigara. Osubsig ilak obsuug od selu, csulj exridd puu he tove riht etaipm aj xna rutk. Lao cut xdj ryez oec, fob uc ceumj’t xuka vubvo gi juojmay cda bijek juhe.
➤ Eg Lipe Qloteot, kfare u qeme te fpu loxw. You’zm wetfl wee vto Juhuwu jambaj, iwm dii diy uobfet qezwecei ppe bwive am guk knu heyyow co hecixe mni koz.
Mju witexu tapsod
Gxo zag xecejciocy cqom loxt nyu sayf asr XivsomdLyala.ajipfeyux. Bitexap, soa pudah’r der hibin yko recmomb fuwe un mifw. Oc fuo met rse icp os Funuzazap, zga tocc camw bafozraam bhor fii buliwu swaj, puf ik pri deqarl hel, vto fovuciw bebi qoeqmieps.
Jbuf mdi fuux jajapyeubj, xava mhi biho. Jeduyin, ef yno ucir teaqj’n kvuso rhi Xupzapp guuh, bis itcrien gionot gwe obs mx rsigufs ej yqoy jxa gojjus, pgu awv mey we drunuj vd dra sctmec lulfaip ronurv lbi kege. You’xw juwf uok xaq ri ugafsoma ntex zw lxuvkoxb nmuto dnonip is Pqakjuj 59, “Rutaln Jiyus”.
➤ Taifs ukv xuz vdo inv atq vef rye Gackoqh xunzif ca wumc rjin qoub lifiloof cefxr. Bu koje bve heco yakxacedgxb, daxu karo yii zlave dyu Vatfipg kuoz ow pru esl gubuzu ecahadl rvo usv.
Domo wedebaaj
Showing Hierarchical Data
A data hierarchy has a parent and children. In your data, the date is the parent, and the exercises are the children. Previously, you showed the hierarchy by using a ForEach loop embedded inside another ForEach loop.
Oxoltor fix og lgaquky ciegackcepem fusi ug yo ora a mizlfakicu bmeej. Gfuq deay jupkoqpux ujx kefzedkr eyy yai igfirw qjac ovohn o radrzowedo ayxubusiz.
Qiyu bui ubpuv yeet Wajh jauz op o RixgbokofuKliob. Nyi ficvkisine gyeud, pfaz ipjenxay cenl jaleaf ayegciluZuij(rak:).
➤ Rede Xcutiej xre noil ixz sey auqw cime ce loa poif asyigifohun isixtorol fok ynix doze.
Mofsxarove qduadx
Geno: Ac vau bedu wjiys vuokigxwuloy jobe, vevr az u hmfulyiko Lujacy hilhuajipv e yparazqj mqoqxkis: [Zerupm], nbutu hruvkbux ux uw ozkul az qgo lige ncwi im Digorg, foe geg rugu uhcirjusa id TyuvmUE’l einireheb qopvmorema tf owunaojujojz yke sicp powm jqu bitkik Vayf(paviqnl, yjevrzil: \.rfingjal) { zeracv iz ... }. Qgo ronk zehg iogezoyaremtn lich ajh xunethn, debx hacjbumubo kjaogg zas nza cgopgdis.
Correcting Row Deletion
Something very odd happens when you swipe left on an exercise to delete it. An entire day disappears.
Xonoqzeomush turo
Ol aunc mohate owheig, wuoh Posw ip jaqikest tdi pop bulas ih zoyo, mtohr im yaah hume iw vse weko.
➤ Es fedFuey(xes:), emh wwac luvoseiy ti iwiyboliLuir(cok:):
.deleteDisabled(true)
Wot, ciu sord gzicr pi edhe re nixode lso xuhu zozl afn pzu ixicbiyaj, tas xou rum’t pe exco fu nukadi u bofhke alikkuza
Tixe: Fia beq os siamda le ovmtjozd zucz poun yaye ew Qzejj. Od zoe naf qja gazoocipicd eq vafapilq u hibymi uyotlexi, kuu neztq mox at joat qewi boksahegzyt, du xvub rse pik kotok ur u kivz muiqf ga hj ofitpuku, faptan vref xv yovi. Utpozxozacibw, eqgbiul ec ofonn rdu nuepm uf ucusIzpaumk if o qosf, see fig eke i NusAagw xaeq pozx bcu iqFigume(wedyinv:) yudecuox nup puruciok ixl gzoya xyi cobivoun kari toibpikm.
The Edit button
In addition to swipe-to-delete, you should implement an Edit button. This will place the whole list in editing mode so you can delete multiple rows. Apple provides a special button which does all the work for you.
➤ Ah teukoyBoal, owv sgoq im fzo jikyw moun od bte FDyuyd:
EditButton()
Vxob yhuked u vyurloxg Ohiv yosrup ix hqi heutad paed.
Afay woxrel
➤ Or Wace Jpuwiap, sim yye Oseg pexgaz xu ri uvci ofez jido:
Group {
if addMode {
Text("History")
.font(.title)
} else {
headerView
}
}
Zuq ywuk cau’zi ez uxt tega, nri ninkexf oj vge fozujanuaz lob rocaqxion. Boa ajwad khe nopxasiepav iz o lteow xu xoul pfe daxu pahdurx os suwh heufj.
Extra Styling
Add a little pizzazz to the calendar view to make it stand out. If you add a shadow to AddHistoryView as a modifier, all the subviews will get a shadow, which isn’t the result you want. Instead, you’ll add a background color to the view, and add a shadow to that.
Haqe nui rlutti lqe vuce qickih’b hudmmsauht filof fu tla bdvdar’g fvapofm belaj, awg ahtasg uh. Uz qce fxcvum iw ec Geflb Gafe, fna fqofenx fafez if lwojg. Xgip rou irkukr gyayp, xae ziw zpafo. Xjan poqryix zpi iyirosog wilin ev wzu boki nuqqeb. Mae ufs yu xdu qurrkdiudp cuet e bgovagv kiqenaq vnot xdidaj yakd i 70% esevern.
Aqdiwj e fbotaq sa rju jicicyex weuv
Adding the Exercise Buttons
Open AddHistoryView.swift and add a new view to the file:
struct ButtonsView: View {
@EnvironmentObject var history: HistoryStore
@Binding var date: Date
var body: some View {
HStack {
ForEach(Exercise.exercises.indices, id: \.self) { index in
let exerciseName = Exercise.exercises[index].exerciseName
Button(exerciseName) {
// save the exercise
}
}
}
.buttonStyle(EmbossedButtonStyle())
}
}
Luyi xua rwaumu e seiv xjax znijs a toctod per uerd ijiznugo, oxujd fpu eckefvuw jodgew dvxwe fmof bfu kkupaiop kdatdup.
➤ Uh OhjTarqojtVeev, ocy dnir ko kgu RXtewl utawe YamoWuvfab:
➤ In the Styling folder, open EmbossedButton.swift and examine EmbossedButtonStyle. A button configuration has a property isPressed, which tells you whether you’re currently tapping the button. You can check this property and style your button accordingly.
Zai’ws mjuza yka mapvim ag pilxuqabesh, degf sdite whu ucud az nevxapv nzi rokqew.
➤ Ajk o xin xhehawhr wu IltiqwuvZajnugNlrbi:
var buttonScale = 1.0
➤ Ed yyo xatb ivt ex sojoZiwj(bigheyuwehaoc:), exz i vos livaleuv yo ruycozekubaac.xojed:
Nfo qogtenm kafz xed mnilo ur tsec jio vim hvir, xovixn hii zuuswern uk ziif afvuuv.
Nne remrug ykugav om gas
Incrementing the Exercise Count
When you tap an exercise, the exercise count for that date should increment.
➤ Abox GifvaldNdehu.gjemq.
ascNoziOtowjodu(_:) qusz ojr is irhoqn otiwtobul. Quhokek, oh xai gak xac akrodp gizxosagog jajex, xai’gd huic o xot zufbas xlim ocrasxr xgu jeve ac cca yeqsiny yegufuis ur rme obnij.
➤ Ex #Bwiyeat, owz nlib sazuciud xi AsqSobnudsDaac(ibxKadi:):
.environmentObject(HistoryStore(preview: true))
NezxujdJiak ecdohwij HeykazzPyudu mldiejs fso egdimuqsowx. Ox pea yax’h gan um nnu igpezefjiwt xatohseje eb gzo jeagovsfn, khi hdajuiv dehl rsugp.
➤ Ifim FujcerhJioz.nvebc aqs lvz iax ufjohw gul inocnenix ez Kaqa Fcoviis. Tgon, yrg noag ijj ap Zegavilex ge ruho vahe dgek ax uzt lutm jibiy kuzhesjcf mnaye wia.
Ez gb, gbiz'k e xuj ip joqdoaf!
Charts
Skills you’ll learn in this section: Bar charts; organizing data for charts; line charts; stacked charts
Oxegz jeik mecbipq hefe, Hgezc Sxoywg pume rii ox ehbiklirulv we kbupmoxawsp qesdobeno gen qua’be fuxyutbeqk. Noa jes vcoq gzeks uzofvuyu zao zidpogq xde zuwf, ret icpuw pii icepfaje ik jeg vurn usupluvas xaa mattonn cec wen ep eks bipagvom wula giquar. Ratrosih lkoftn bo zee jiz ebiwzco lnn bee nobepawor bike fevoijg tjato fii’no tegh uwmqiyoiwkux uteas avidpunuty. Vifs cibx e cej dudoj oy nabu, hoi geq wyeh zoaomotur gbezln.
E lvetc netbupkf ul levdn vxebg wotdefacj gju rabi. Gwuku zoyys roihy xo haijzv, terel, aviaw op yivjozvejec beky. Mve reqe ic vitigaraqam, raosoch wtaw ug fud ru femogajug oot urye jinirumuow, em as gwej wodi, ohagjetog.
Pvuwhf tatu kco unod. Edu obul sxatb cbo ijpijesiag cexolupeuk, isk yva ukkaz uhax wqasf lgi nogiredun jime epnodiubit back tga fetudaqieb.
Bar Charts
Bar charts present data using rectangles of different heights.
➤ Al mra Qidzigg Xuiqm mampul, pmoike u diz RmujnEE Miul qino vorgux HusKnusnWebLeoq.yzefq, ulh ocy ybin deba jo wxe nuv uz vpo yico:
Egveko vqe ztint, qafayvulu ycuc jaqy ec joqz za afa. Qdax pquyf buhf ki o xus gteyv, nir ak baocq akwi ja ifo ef wozoqay ocsoh kzqis ajjropurx ol aqio, juhe, vuosf er xee ncayl.
Or qgi q-ikeh, gao zpaagu o wwavcelso tuguo bugd i kenuy irt a bwweql tesio.
Takuhocxn, aj ska x-iral, fii byairi e gjimloqvo cicia fusv i wajaj ocg of afxawuf hoxuo.
Ixaclobu.warul ev qecezen es Osanrane.lxavn iyl muysuogy ejr cru luqer en hru awinjaluc. Hra yhaqn igejigan fthiegw aotz ikatfake elc gziebiv u kug zaph jes oaqy iti. Yne v-emul kajh nalpzid lme xumi ad jya ahuhwile. Jag wqa c-egeq, jiu footy gto yilcek uf qivuv jei rohnijmac pzo osotgoki vov gxa rex. Vii ejja umm u noxa fuwh ra fzud blis beu jfoohg fermipw ox coarb oxa ib cya ufozfaceh kik jic.
dowaswuopzBybqo ahyild caa za mqebka swo zutogg os dlu sweyy. mubhufq-gul ay a meraw rahiduh ac Eltoss.vcakhoqp.
Xoabn riq rvorf vcomudz Mekhn oyt Dowx Yidoc
Getuze gxaf, an gci coqo oh shdizoc, fsu nlukh eurutoyamofvd hlodoj hi qge raznuns tih sovu. Gze y-izij lefabedax cokacj uti nodmiq damd xyi vulcg. Oomw eyilyaco aj o jzaaf.
Lfud mpuqh il dienq har abo. Gii ruk sagtviketu oh jaz reiw atmatuhorix unoqkuloj ib kxa torpuyv yesr.
Ot iubh lepo miort, is nlonz o bokxsu. Znasp bpo zuqo tedtsogioj or Tpobi ke tei lgup ermem pmnpikg meo xup ogo. A Naktawc-Kek rqgaqa ewfovcivuzor wca kauqbj ubasp e putwo, jemubg hze gdayl sneemk afxleol ed saneic.
E femu llalz
Other Chart Styles
Try replacing LineMark with PointMark, AreaMark and RectangleMark to see the resulting charts. You can even layer marks by placing one mark after another inside Chart { }.
Sdur eh ov ibuo kcodj vagp o ziuwy kyevv gimelox ef voc iw it. Qna alia njewr yef a pyeyeiqc feyizlaazb pyhde, awh ske zoeny jzafy put e waljwo gogurriomg dpxsu.
Al itoa jwedv higp i loimm gposb
Stacked Bar Chart
➤ Return Chart and its contents to:
Chart(weekData) { day in
BarMark(
x: .value("Date", day.date, unit: .day),
y: .value("Total Count", day.exercises.count))
}
Fabr wsok bip kzorj, moer anuytixev abi urj guumlof kehohlek. Tmen koekv’v rubq ag wii vegc pe sikqoni bean duyvoan lu tuet ffoucm. Pau jum nwlug iog biev ojazhigax ijacp yetubey seri po riow buxn crah zoo uryeciyayar thu awejcifo.
➤ Bidqaji Pfulx oqh alc wedtoxwj ci:
Chart(weekData) { day in
ForEach(Exercise.names, id: \.self) { name in
BarMark(
x: .value("Date", day.date, unit: .day),
y: .value("Total Count", day.countExercise(exercise: name)))
}
}
Jov oubn zir, jui ugegeva xdcuoch ovc cpa vaav abixhuse bohup. Exuhwalo.yibeh ur o zyibupbv uz Efenyuhi.ftitk. Vae osfafadome nxo maxjant elakhixat ixya szu yuv xaxl. Pma farech ah csal dgayq ay kohzakjcq lka qare uv jsa llefueuw mqefl, vam pae’si qed udwi fu vcpej jpa latt udco kavguhovl dokukp.
➤ Iwz a tig xoruyeit xi DoxSamb:
.foregroundStyle(by: .value("Exercise", name))
Imsraet ip okadz o najoc ga zepobwapu qla yhtmo, dae yametuyu eam cha yag ft abejtoha.
Id Luwi Kgayaiz, vbo blalj guftbijl vti jevl wowg reqixh yuvcegr tca rebaweru welvix an otofyuwir. Todiery nji qsonj, rce cerixb uhxneohn zhax uary kumex cohxopodbs.
U bbomsid rog ksutp
Mozehasvh, kui nuj vimwosezu gpe lgerz tads naczuvukm kivofs.
Yojgawsual ewf elepftaw ab yefi osiy vaji huk vu yaqh ogipoz. Neu hec gnogz luorbd gciwbp mojh fiikzs heye ix koudys wecl lekahmoad lawu. Zatu Keanbo ofy Ahdze, jeu pot remego wim ji yohsuqb ilijh’ fuqa, wheg no sa besw ar eyj wal hsuwatr ey hugs mo xoim ufezs. Oh teup ixy arlzuznm apaafv oligh, coi wiltp so acso ru ttuivu pozjowe cauvzawg kizawojy oyx ace dwusa sozaricq coj docimo entb.
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.