Appendix G: Body Readings (Shop Manual)
Status: Live on the App Store. GPL v3. Public repo at github.com/fluhartyml/Body-Readings. Build-Along chapter: Part VII Build-Along 06. Body Readings is a focused vitals dashboard: heart rate (HealthKit), step count (HealthKit), and live motion (CoreMotion), three reading cards composed by a single @ViewBuilder helper. Book 19 (Custom Views and Modifiers) reaches for this app's readingCard(label:content:) function as the textbook example of the generic-helper-function flavor of view extraction.
What it is
A small iPhone surface showing three live numbers: heart rate from the user's HealthKit data, step count from the same store, and a live motion summary from CoreMotion's device-motion stream. The app demonstrates two adjacent Apple frameworks back-to-back (HealthKit for stored biometrics, CoreMotion for live sensor data) and shows how the same SwiftUI card layout can hold either kind of source.
Architecture overview
Five Swift files. The whole app is a single screen with three cards plus the two store classes that feed them.
- App entry:
Body_ReadingsApp.swift—@mainscene. - UI:
ContentView.swift— the three cards laid out in aVStack, each rendered through thereadingCard(label:content:)helper. - HealthKit store:
HealthStore.swift—@Observablewrapper aroundHKHealthStore. Holds the authorization handshake plus the latest heart-rate sample and the day's step count. - CoreMotion store:
MotionStore.swift—@Observablewrapper aroundCMMotionManager. Streams device motion at a low rate and publishes a compact summary value the UI shows. - Data shape:
Item.swift— the small struct used to model a single reading (label + value + unit + timestamp).
The readingCard helper (Book 19 reference)
The chapter's point: SwiftUI gives two ways to extract reusable view code — a named View struct or a @ViewBuilder function. The function form is the right reach when the extraction is shallow and the surrounding context already knows the data shape. ContentView uses the function form:
@ViewBuilder
private func readingCard<Content: View>(label: String, @ViewBuilder content: () -> Content) -> some View {
VStack(alignment: .leading, spacing: 8) {
Text(label)
.font(.headline)
.foregroundStyle(.secondary)
content()
.font(.system(size: 48, weight: .semibold, design: .rounded))
.foregroundStyle(.primary)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
Three callers in ContentView:
readingCard(label: "Heart Rate") {
Text("\(healthStore.heartRate, specifier: "%.0f") bpm")
}
readingCard(label: "Steps Today") {
Text("\(healthStore.steps)")
}
readingCard(label: "Live Motion") {
Text(motionStore.summary)
}
The function takes a label and a content closure annotated @ViewBuilder. The body of each call site can be any SwiftUI expression. No state has to be threaded through the helper because each card's data lives in the closure's enclosing context. The helper is a layout, not a component — it doesn't know what numbers it's wrapping.
HealthKit pipeline
HealthStore handles the standard HealthKit handshake:
- At first launch, request authorization for
HKObjectType.quantityType(forIdentifier: .heartRate)and.stepCount. - On approval, start an anchored object query for heart rate (delivers new samples as they arrive) and a statistics query for steps (one number for "today").
- Publish the latest values to
@Observableproperties thatContentViewreads.
The store handles the cases where authorization is denied or pending — the cards show placeholder text rather than crashing or showing zero.
CoreMotion pipeline
MotionStore uses CMMotionManager with a slow update rate (a few times per second — the cards don't need 60 Hz):
- Check
isDeviceMotionAvailable. - Set
deviceMotionUpdateInterval. startDeviceMotionUpdates(to:)on the main queue.- In the handler, compute a compact summary (gravity vector + user acceleration magnitude) and publish it to a
summarystring the UI shows.
The store stops updates when the view disappears, so the motion stream doesn't keep running in the background.
Build + deployment
- App name: Body Readings.
- Platforms: iPhone (HealthKit is iOS / watchOS / Mac Catalyst only; native macOS doesn't get this app).
- Frameworks: SwiftUI, HealthKit, CoreMotion.
- Required Info.plist keys:
NSHealthShareUsageDescription(read access to heart rate + steps),NSMotionUsageDescription(CoreMotion device motion). Without either string, the system refuses authorization with no UI feedback for the user. - Capabilities: HealthKit capability enabled in Xcode (turns on the entitlement).
Known gotchas
- HealthKit on Mac is Catalyst only. Designed for iPad on Apple Silicon does work, but native macOS doesn't ship HealthKit. The app's deployment is iPhone-first because of this.
- HealthKit authorization is per-data-type. Requesting access to heart rate doesn't grant steps and vice versa. The store requests both at once; partial denials mean partial UI.
- CoreMotion battery cost. Even at a few-times-per-second update rate, leaving device motion running while the app is foregrounded draws noticeable battery. The view
onAppear/onDisappearpair starting and stopping updates keeps the cost bounded. - The
@ViewBuilderhelper is private. It lives inContentViewas a private function rather than a top-levelViewstruct, intentionally — that's Book 19's worked example of the function-form extraction. Promoting it to a struct would change the chapter's reference.
Sources
- Project source: github.com/fluhartyml/Body-Readings (verified 2026-05-10).
- Build-Along chapter: Part VII Build-Along 06.
- Book 19 cross-reference:
readingCard(label:content:)as the function-form view-extraction example. - Apple Developer Documentation: HealthKit reference, CoreMotion reference.