Skip to content

Mapping — Apple Health / Health Connect

Source: Apple Health export.xml. Pillars: A + B. Mapper: mapAppleHealthmapAppleHealth(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 / BodyMassMeasurement (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)
HKCategoryTypeIdentifierSleepAnalysismultiple category Measurements (sleep_stage) over adjacent intervals (§4.3 — not a sampleArray)
HKWorkoutSession + a continuous WorkUnit (distance + energy + time), disciplines from workoutActivityType
sourceNameprovenance.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 ConnectOpenBody
StepsRecordinterval quantity Measurement
HeartRateRecord.samplessampleArray (or discrete quantity)
SleepSessionRecord stagescategory Measurements (like Apple)
ExerciseSessionRecord (+ laps/segments/route)Session → Blocks/Exercise; route → location sampleArray
record titlethe 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.