How BodiLog uses HealthKit and SwiftData together
BodiLog is our weight tracker. It writes to and reads from HealthKit, but also keeps a local SwiftData store for trends, charts, and offline use. This dual-source architecture sounds simple and isn't. Here's how we make it work.
The data architecture
Two stores:
- HealthKit - Apple's health database. Lives on the device, syncs via iCloud if the user has it enabled. Source of truth for weight entries.
- SwiftData - our app's local database. Mirrors HealthKit's weight entries, plus app-specific things HealthKit doesn't store (notes, photos of progress, custom metrics).
HealthKit as source of truth
When a user logs a weight in BodiLog, we write it to HealthKit first. If that succeeds, we mirror it to SwiftData. If HealthKit write fails (permission denied, device space, sync conflict), we don't write to SwiftData either - we surface an error and ask the user to fix it.
Reads are HealthKit-first too: when the app launches, we ask HealthKit for the last 90 days of weight data and reconcile against SwiftData. If HealthKit has entries SwiftData doesn't, we add them. If SwiftData has entries HealthKit doesn't (rare, but possible), we re-write them to HealthKit.
SwiftData as cache
Why mirror at all? Two reasons:
- Performance. Charting 6 months of weight from HealthKit on every view is slow. SwiftData with a single fetch is instant.
- App-specific data. User notes, photos, custom tags - HealthKit can't store these. They live in SwiftData attached to a HealthKit UUID.
Conflict resolution
The hardest case: the user logs a weight on Apple Watch, the watch syncs to HealthKit a few minutes later, and BodiLog opens and sees an entry it didn't create.
Our rule: HealthKit wins, always. If we see a HealthKit entry without a matching SwiftData row, we create the SwiftData row and mark it 'synced.' We never delete a HealthKit entry from SwiftData reconciliation - that's destructive and could lose user data from another app.
Privacy guarantees we make
HealthKit data is sensitive. Our explicit guarantees, surfaced in onboarding and on the App Store nutrition label:
- BodiLog never sends HealthKit data off the device. No analytics, no cloud backup, no support uploads.
- We request only the permissions we use: read + write for body mass, read for height (BMI), no other categories.
- iCloud sync of SwiftData is opt-in. Off by default. When on, only the SwiftData store syncs (HealthKit handles its own iCloud).
Read more
On SwiftData specifically: migration patterns. On the privacy nutrition label setup: demystifying the label.