Mapping — Apple Health / Health Connect
Source: Apple Health export.xml. Pillars: A + B. Mapper:
mapAppleHealth
— mapAppleHealth(xml) → records[]. Pillar A is mature here: discrete and interval
quantity samples, sleep-stage category series, and HKWorkout → Session all map with no
model gaps.
Structural correspondence
Apple Health (export.xml) | OpenBody |
|---|---|
HKQuantityTypeIdentifierHeartRate / StepCount / BodyMass | Measurement (quantity), type → canonical heart_rate / step_count / body_mass |
any other HKQuantityTypeIdentifier… | Measurement (quantity), type → apple:HK… (lazy token) |
a daily interval aggregate (startDate ≠ endDate) | quantity Measurement whose window is startTime/endTime (§4.3) |
HKCategoryTypeIdentifierSleepAnalysis | multiple category Measurements (sleep_stage) over adjacent intervals (§4.3 — not a sampleArray) |
HKWorkout | Session + a continuous WorkUnit (distance + energy + time), disciplines from workoutActivityType |
sourceName | provenance.device.model; method: "sensor", sourceApp: "apple" |
Output examples
A discrete heart-rate sample → a quantity Measurement (count/min → UCUM /min):
{ "id": "apple-q-1", "recordType": "Measurement", "subject": "subj-001", "type": "heart_rate", "quantity": 64, "unit": "/min", "startTime": "2026-06-20T07:00:00Z", "endTime": "2026-06-20T07:00:00Z", "provenance": { "method": "sensor", "sourceApp": "apple", "device": { "manufacturer": "apple", "model": "Apple Watch" } }}A sleep stage → a category Measurement over its interval:
{ "id": "apple-sleep-7", "recordType": "Measurement", "subject": "subj-001", "type": "sleep_stage", "category": "asleep_deep", "startTime": "2026-06-20T02:10:00Z", "endTime": "2026-06-20T02:48:00Z", "provenance": { "method": "sensor", "sourceApp": "apple", "device": { "manufacturer": "apple", "model": "Apple Watch" } }}An HKWorkout → a Session with a continuous WorkUnit:
{ "id": "apple-workout-12", "recordType": "Session", "subject": "subj-001", "disciplines": ["running"], "intent": "train", "startTime": "2026-06-20T06:30:00Z", "endTime": "2026-06-20T07:12:00Z", "provenance": { "method": "sensor", "sourceApp": "apple", "device": { "manufacturer": "apple", "model": "Apple Watch" } }, "workUnits": [ { "id": "apple-workout-12-wu", "recordType": "WorkUnit", "scoring": "continuous", "performance": { "distance": { "absolute": { "value": 10.0, "unit": "km" } }, "energy": { "absolute": { "value": 520, "unit": "kcal" } }, "time": { "absolute": { "value": 2520, "unit": "s" } } }, "links": [{ "type": "measuredBy", "ref": "apple-q-1" }] } ]}Health Connect parity — not a separate mapper
Android Health Connect maps the same way and needs no new model features, so it is covered by this mapper:
| Health Connect | OpenBody |
|---|---|
StepsRecord | interval quantity Measurement |
HeartRateRecord.samples | sampleArray (or discrete quantity) |
SleepSessionRecord stages | category Measurements (like Apple) |
ExerciseSessionRecord (+ laps/segments/route) | Session → Blocks/Exercise; route → location sampleArray |
record title | the v0.3 name |
This shared mapping is itself evidence for the model’s neutrality: two of the largest consumer health platforms reduce to the same canonical shapes.