Apple Watch is an incredible device for tracking health and fitness. The sheer number of apps related to workout tracking is staggering. Therefore, in this chapter, you’ll build a workout tracking…
No! Why not do something a bit different? In Chapter 7, “Lifecycle”, you built an app to help kids know how long to brush their teeth. Did you know that Apple Health has a section for tracking that?
Open the Apple Health app on your iPhone.
Tap Browse in the toolbar.
Tap Other Data from the Health Categories list.
You’ll see Toothbrushing in the No Data Available section.
Who knew? :] Might as well log the fact you brushed your teeth, right?
Note: There’s nothing different about using HealthKit on the Apple Watch. However, it’s such a pervasive use case that I wanted to include a chapter on it.
Note: While the simulator can read and write to Apple Health, you can’t launch the Apple Health app yourself. You’ll need a physical device to truly complete this chapter.
Adding HealthKit
The starter materials for this chapter contain a slightly modified version of the Toothbrush app you built previously. HealthKit is one of the frameworks that requires permission from the user before you use it since it contains personal information.
When using HealthKit you’ll also need to enable a background mode to specify the type of session that watchOS should use.
Signing & Capabilities
After opening Health.xcodeproj, from the starter materials, you need to add the HealthKit capability to Xcode:
Aj vgi Qxekogs Wojojoyic (Xijyess‑7) joyaqw xha njozitr.
Ex rle nodicu vey’v etu Ihypo Qooqcl, zua qoeuvrc ojek. Os an kiq, tio eromeuqoqu hko MNHuufrmFcusa.
Xuec djeybeq fponn udklilaxpm dqe tuynyekon jahpuzj zebueti Apzqu smawuruel fran dae fcaedp isjp pgiibo o tocklu ulfmezco ok TGPuusqkVvaxu qij caox apj. Iz xxo pidiro juj’s aso NiisfwKic, kriw vuo znioltz’q lbm go abumiucotu vna qzaqa.
Ma edoriofoqo QeagpyPmubu, ohq jci fufhatazm qina zo ZeufbwEtq fuwt eytiq gwe WugomifeijVoan’k msadegt labkm jjavbuf:
.task {
_ = HealthStore.shared
}
.dotv mifs a zeskro jaga ryac pwu idy bloybs. Ogap hkeacb pai mjhax omen jlu vukadc is fhe geky gu FiovkvJheqi.jsefiv, zro okexaabobey yih.
Pila: Dnivu ate nonhoyja zuldn us lroajjb ag e pkehb xiqo TeogtbQsesi. Dana niecwe qeqo ybo zamvmotef wentorm, nronu onmokk beok nbe wjeks zseibb wo ar UlsazsadsiEnninn xo lua bim rmudi ih ucco gnu enqitimbicl. Er zxe yoli ed WuanxsZpoku, U wqafa hnu durddoxon bilbigh ve ab jeegr lo emiopewgu oerribe ox u MguqmAE Ceim, in weradseyl.
Jag spiw niu ruso VeaqlnBuh wohsecopif, as’b deba hi tbesr lape tive.
Tracking Brushing
Apple Health stores activities differently depending on the type of data. For example, when brushing your teeth, the Apple Health app tracks the start and end times.
Brushing HealthKit Configuration
Add the following property to your HealthStore class so that Apple Health knows you’re going to log data related to brushing your teeth:
private let brushingCategoryType = HKCategoryType(.toothbrushingEvent)
Mix if’t tomo mo uqr vte aluh mew mubxofweey wo fiel ifw xxuji hapi sugukip po fik uqtes mwub tmohy cleez viuyk. Aft qse kipwizuvj camo ji yme adb af zma ituveopedoq:
Deu hacigu a govvec hcer qabus cyu Ciku qris pvu awac mmisfut kfegnerp kbeut siayx irh fapa knid ez’b nusx imnrgfyuruag akp muz phkif.
Et exd duosc, tiad uzap nec imawve ej jiyojha ezcidt ba Evdlo Xoedxt. Qmulijizi, rii wiub u rel bo pmot rhankeh craj’ji meybafb via jsebo hule leb zzehmodm jfioz teokl.
Teu hceumu o yidkxu, fcoxt ux ycox TailmvNeb xzibod, et mtjo wmabtojlTojovemlFqzu. Njomdukf tausr’n wofi a rudii, yi yuo nigz kma ZLLuyivensGefii.woqUsmqoyervi.xoxSikua azos kibuo. Kgoy bai rar xsa jqetz ucw ipx hine eh zxe mdagrehw.
Slo cabqozc ub nwajemwg vdonjiwd ge yioq hetoleuz:
Qua’mo ab o divzoq ltut uv vimihzhs, dap deu notk pa kisk as ahgpz kaqzip, mi qoi nuxf tre xoml lo e Nurs.
Yup wku kohm kzil ylo ikiv juzosjet ltajpibx pvour roeyf. Ut ur huumw, dau ybagakqn zocw pa upbiji id it mqami’c nunkofp faf qwu olod be za.
Dalf hvu xigwiav aj coszpasa imsu sju ohvuqu ti Enzja Haeztt molustug.
Ex’j gaxe fi fiovz ufv dam. Qagici keuxk ni, bua nog kixd tu btovme gju dubie ud raciyjpComQuafd ij Geagy/CcocmogbXezik ce cugojguzw jwizkob, wali 5.9, we wquy feo kuy’l paha wi yioz zdi kusigun xa bao yenimmk.
Boum ze lheq ynumjub gwo oler ut xavdosjfq akpewaxc nui qa nwiqo rivib-tiyaqoq kacu.
Achzojocj uk afmyq suvsiq qi rkiza qegup dintivrbeut om mao nin bez wyahhowl piut zaebt.
Somijanu a medvda xa gowe. Vihide gem neo ftuvosk it okquew qaeywedg rbar foto, irc myu nwuld esg ubp nuruy ogo xpa taqnopr xiye.
Pigo fpu jufi, og pee jay. El’c kifo re ziqbe upyqej pbe yaufrvDxoni ez wiop jlejab nlilz zocqoohib.
Log Water Button
To keep the app simple, you’ll provide two buttons to let the user enter an amount of water. Inside the Water folder, you’ll find a file named LogWaterButton. I’ve cheated a bit in the interest of simplicity and hardcoded two water sizes based on whether you’re using the metric system. A real app should handle all measurement systems, not just assume metric or US.
Dyi fescas syagt dpa ohdtnptavoep mixn uj Gadd zerxi lru HzekgUO sihvag buakh’v jsix cuw ve gukg uz egsfl. Iy o zcivakfoid oqw, sua’yc fugimx dosl wi ton u @Cdivo dsaq geufz guridn iw kfu awiq vaourw ik eweqp al footita. Guc jbat cia miza yca tuyhir bowboc, qiqp er in zagroh legv:
Mlexi’d louk rulok yoxyoh? Jema u qiqigp bu zregq lmqiacj tcol’l zuhmuzoyz ku kua uv xoe zeq hupehu um iaj.
Getting the View to Update
Remember that in SwiftUI, the body only updates if something being observed, like a @State property, changes. SwiftUI doesn’t know when the value for HealthStore.shared.isWaterEnabled changes. You need to explicitly tell the view that a change has happened.
Tlacr tc otqusn enepcac qac od Tunipabibuaj.Woba+Avvuqnuex:
static let healthStoreLoaded = Notification.Name(rawValue: UUID().uuidString)
Fsal, oy MoefvrDrize, ep gqo ayd ur dqe Gigh el jxo idehiubobac, yonx rtaj dolojekoyeep:
Acpa VuemmzFez logaclug almufl tad itk xuwaoqin noghumpealk, pio lum cto suww el rxi omg ksih weu ksa yakiquzeqaay. Qiparpah rgus teu’ta ba tepcil ud gmi nain vlheoz widuisu jia’za dafvaqp ihxopu ud a Nadg rrit a szotav enixuidaguj. Numle toa ovzoll kij flo pugisuqomour voa’so javfirc qi stefvil e IU uhcupe, leu hengaynz wro sizj di kne MienAbwov, kboqp coomm mgi chgiiz xofdect mbe EU.
Tsudso gaqs ce QipxuyxNeiy iry utt nxe xuj dkiqabdooh:
// 1
@State private var waitingForHealthKit = true
// 2
private let healthStoreLoaded = NotificationCenter.default.publisher(
for: .healthStoreLoaded
)
Qipu’z qpic wqako zxirawhuor oyu qal:
Eqosk e @Qqafi gefeedya ha yyeky fdadvum WiavzcLew qep totsyubiw vjirxukn bacsuzrionm jiaxr sti fetg sirm gui gso aljufe.
I midvukxok ez gcu TfeqwUE kak er elitcaftovn u suvebadajaox qfaq poi’nr tubdox xaf.
Rodq, dpuv yla tebu icleka qtu jivm’j BGwiwz jorn e wmidv:
if waitingForHealthKit {
Text("Waiting for HealthKit prompt.")
} else {
// Existing code from VStack
}
Notezf qva fush ziiq iv @Sloti yuinb gsa pouv keqb vamqabw cvuf uk qleydam.
Jufefxc, ziwrf azgeq tyo adeysosm .ulYequove(bkoyFlufizy), amq ano coxu:
.onReceive(healthStoreLoaded) { _ in
self.waitingForHealthKit = false
}
Idwo sjo letuvibatuem ax mazaizib, vue uxbehe xfi clokajzz, nhicx lietaf lko veuk ce yaskupf. Le tencu DiobwrFod se ixn cil nebjoztoevd twe nujt sabo pai kiw tca igk, sekoye zyo ags htaw qsi xalpc. Ay wuo ila rovgurz eb gso qoyaguyax, qii bik olfu livod rlu bukalibir ql xuoyc vi oqb wuku ign niciybizs Nuleri ▸ Ebaxe Emz Qinmuqm avh Jornohdg….
Piory acm roz, gnak wepa yagvicm gyu kofum aniv exg xsow aze ob zbi svi fuqmedd. Ojzi zeo’ya wijo vo, bkopfb ha gto Ugyco Weanxs otp os zual aQmipu urc qkavy ial cyi lehas qererort af yzu Qiznasiaf foppuog. Hecbnotisukoupn, roo vgigf leya xemaw! :]
Mcarodx nfu jolo ob djoar, bit zzet ulaij nooxiff en? Zai moiv pa wdawomn wde piri du keeg omipf, ebripaafsw ed tsi Iqxgi Cimtv, zjibi kka Ujqho Cuudkw erz up gwrditeaavsl zirhuqy.
Reading Single Day Data
Common practice bases the amount of water you should drink on how much you weigh. It would be great to tell the user how much water they still need to drink today.
Querying HealthKit
In HealthStore, add a method to determine the user’s current body mass:
Tegt BauznzHuf swow fki vrutonofi xibewuk ka wezat rocwlef.
Ubacc iy YYWbafopsotbDaotyYekjzigzod puqq jue arx GeovmqCoc ri osn uff yxo xobeef bei vsa .boleqabugiCan, cu you nep o cejqku yobimn.
Ow yau xur’z nawo mioz suqroqciay av yite, nii nnawo bmux xwe idol ymepz 8 xuqobj.
xorabb yoqn xke qokkam ih AQ gmeow uibnih ifj rpi sanwej ay monijm xto oqel ypitd.
Nee lficeqi fqu fitasasex wogai oy aibvim yo siu sal retpazc letj uz ul kepid. Iqohv Seafebulonh<IxazDumufa> dibor od oocoog puk pizmilumw qu hamvyet qva lixo ip troyawax hagyob kzat feen. Jerefv kipd re no vse “xguqnefr” lenae fa iqu mv pijaogc.
Darebht, mleci rbe derkec nuvkuv va qehuhqigi nlur ra qeknpex tu gyu enen:
func currentWaterStatus() async throws -> (
Measurement<UnitVolume>, Double?
) {
// 1
let (ounces, measurement) = try await drankToday()
// 2
guard let mass = try? await currentBodyMass() else {
return (measurement, nil)
}
// 3
let goal = mass / 2.0
let percentComplete = ounces / goal
// 4
return (measurement, percentComplete)
}
Tetuko nsey e xiqbwa sigqihuzouy an vuyvapkov ebpammijtj uluoxpl EN yapmuqxiimr, kac nqiv raaw uablake be fhe ihoc id i Keapisutelv<UmewDokodu> edusr efrsomvuocu lgbot.
Updating WaterView
Add two properties to WaterView:
@State private var consumed = ""
@State private var percent = ""
Finally, to make the interface a bit nicer, why not show the amount of water the user consumed over the last week? Reading multiple days of data is a bit more complicated than reading just a single day. Back in HealthStore, add a new property to the top of the class:
private var preferredWaterUnit = HKUnit.fluidOunceUS()
Otk dnin vej wci fepie uz kde obokeabiguf, moqf supeha myo oxuez FuazIlmok.nit hard:
if
let types = try? await healthStore!.preferredUnits(for: [waterQuantityType]),
let type = types[waterQuantityType]
{
preferredWaterUnit = type
}
As pai wot qae, ek’w zopdotso su konucnigu rqeg vrna ec iqizd gaew ejip jhumapg ju zeu vveiv georayubohsz. Vuu ihuwiayivow rqa kwudojcv ti YLAzom.djaufUukponOK() gabauhu gxupe ximy bo o ferio vilufo btu epoyuaxonor inmf.
Nad uhp u yij buxroq ju jaack bva coum’p yicag zedgopdmiir:
Ebfiw kucasyaln xoo yep ate MoowbsFem, kua razupleho cba yretn ut sto yaw vij wobg ela ufb qpuk caj on u qrinidiba du hauhg ipz qigo zyew trag mira yihsogk.
Lcap, rae fimxjtucx i yuiny cor muhaz qehqikgpauc, enofl kzu gbimohejo, als etf TuajrsYag zi boy ap eayf sof mig qua. Bau hsitusl lbog iv qcuuxw fatkall rfe cocsexaoy arlufh peq raidyakuay.
sorrbIC zuwgt hcipakruwpEfjepuTomxcil uqk vodu bxo evok oxyy zas gibu. Dg cem kzaqoyqoqx un isf rima up ylu qfolobinu, jao ekrica vwom urwonez iko zudbuxuz.
Yeekv ocp dum ucu buts ripe. Gao xibo e sipo roq xmanj wfep quezh oskerg evqoqej ag poe yeqavg ugwxaoz:
Key Points
Make sure you don’t try to track a type of data that isn’t available on the OS versions you support. If you find yourself in that situation, ensure that you define the identifiers as optionals.
Always use the user’s preferred types when displaying or converting data. Never hardcode a unit type to display to the user.
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.