Skip to main content

Health

Read data from the system Health app. Requires the "Health" permission — the first call shows the system prompt; if the user denies, subsequent calls fail.

You can re-request access anytime in the app under Settings → Health Permission, or jump to System Settings to manage each category individually.

Steps / Distance / Active Energy / Flights​

These APIs return the cumulative value from local midnight to now as a JS number (not a JSON string). Results are cached internally for 5 minutes to reduce authorization churn.

APIUnitDescription
Health.getStepsToday()stepsToday's step count
Health.getActiveEnergyToday()kcalToday's active energy
Health.getDistanceToday()metersToday's walking + running distance
Health.getFlightsClimbedToday()flightsToday's flights climbed
const steps = await Health.getStepsToday()
const distance = await Health.getDistanceToday()
this.steps = `${steps} steps · ${(distance / 1000).toFixed(2)} km`

Steps Over an Arbitrary Range​

Health.getStepsBetween(startMs, endMs) returns the step count for a given range. Both arguments are millisecond timestamps.

const now = Date.now()
const yesterday = now - 24 * 3600 * 1000
const steps = await Health.getStepsBetween(yesterday, now)
console.log(`Last 24 hours: ${steps} steps`)

Heart Rate​

Returns a JSON string with the shape { value, unit, date }. date is a millisecond timestamp.

APIDescription
Health.getLatestHeartRate()Most recent heart rate reading (bpm)
Health.getRestingHeartRate()Most recent resting heart rate (bpm)
const result = await Health.getLatestHeartRate()
const { value, date } = JSON.parse(result)
this.hr = `${Math.round(value)} bpm`

Example response:

{
"value": 72.0,
"unit": "bpm",
"date": 1700000000000
}

Body Mass​

Health.getLatestBodyMass() returns the most recent body mass reading in kg.

const r = await Health.getLatestBodyMass()
const { value, date } = JSON.parse(r)
this.weight = `${value.toFixed(1)} kg`

Today's Activity Rings​

Health.getActivitySummaryToday() aggregates the Move / Exercise / Stand rings and their goals into a single JSON string.

const r = await Health.getActivitySummaryToday()
const a = JSON.parse(r)

this.move = `${Math.round(a.activeEnergyBurned)} / ${Math.round(a.activeEnergyBurnedGoal)} kcal`
this.exercise = `${Math.round(a.exerciseTime)} / ${Math.round(a.exerciseTimeGoal)} min`
this.stand = `${Math.round(a.standHours)} / ${Math.round(a.standHoursGoal)} h`

Return structure:

{
"activeEnergyBurned": 320.5,
"activeEnergyBurnedGoal": 400,
"exerciseTime": 25,
"exerciseTimeGoal": 30,
"standHours": 10,
"standHoursGoal": 12,
"date": 1700000000000
}

If the user hasn't set an exercise or stand goal, those goal fields return 0.

Workouts​

Health.getRecentWorkouts(days) returns workouts from the last N days, sorted by start time (most recent first). Pass 0 or a negative value and it defaults to 7 days.

const r = await Health.getRecentWorkouts(7)
const workouts = JSON.parse(r)
this.count = `${workouts.length} workouts in the last 7 days`

Each entry:

{
"activityType": 37,
"startDate": 1700000000000,
"endDate": 1700001800000,
"duration": 1800,
"activeEnergyBurned": 280,
"totalDistance": 4500
}
  • activityType is the raw value of HKWorkoutActivityType (e.g. 37 = walking, 52 = running).
  • duration is in seconds, activeEnergyBurned in kcal, totalDistance in meters. Missing fields are returned as null.

Last Night's Sleep Summary​

const result = await Health.getLastSleepDetail()
const obj = JSON.parse(result)

obj.sleepDatas.forEach(item => {
console.log(`${categoryName(item.category)} - ${formatDuration(item.seconds)}`)
})

Return structure:

{
"startDate": 1738854640763,
"endDate": 1738879690763,
"sleepDatas": [
{ "category": 4, "seconds": 3000 },
{ "category": 3, "seconds": 15030 },
{ "category": 2, "seconds": 2700 },
{ "category": 5, "seconds": 4320 },
{ "category": 99,"seconds": 22350 }
]
}

Category Meanings​

categoryMeaningNotes
2AwakeNot counted toward total sleep time
3Core sleepThe bulk of sleep
4Deep sleep
5REM sleep
99Total sleep duration (excluding awake)Aggregated by Omni

Full Sleep Segments (Last 48 Hours)​

If you want to draw your own timeline or analyze by segment:

const result = await Health.getSleepData()
const arr = JSON.parse(result)
console.log(arr.length)

Returns an array, where each item is a time segment:

{
"startDate": 1738854640763,
"endDate": 1738855510763,
"value": 3,
"sleepState": "sleepCore"
}

sleepState strings correspond one-to-one with value:

valuesleepStateMeaning
1inBedIn bed (not asleep yet)
2AwakeAwake
3sleepCoreCore sleep
4asleepDeepDeep sleep
5asleepREMREM sleep
Sample response (first few items only)
[
{ "startDate": 1738854640763, "endDate": 1738855510763, "value": 3, "sleepState": "sleepCore" },
{ "startDate": 1738855510763, "endDate": 1738855870763, "value": 4, "sleepState": "asleepDeep" },
{ "startDate": 1738855870763, "endDate": 1738855900763, "value": 3, "sleepState": "sleepCore" },
{ "startDate": 1738855900763, "endDate": 1738855990763, "value": 2, "sleepState": "Awake" },
...
]

The full data is usually dozens to hundreds of segments — console.log it and check for yourself.

Example: Show Last Night's Total Duration​

const result = await Health.getLastSleepDetail()
const obj = JSON.parse(result)
const total = obj.sleepDatas.find(d => d.category === 99)?.seconds || 0

const h = Math.floor(total / 3600)
const m = Math.floor((total % 3600) / 60)
this.sleep = `${h}h ${m}m`

Put ${sleep} in a text component → shows 7h 10m.

Notes​

  • No Apple Watch / no recorded data: sleep APIs only read data written by Apple Watch and may return empty results. Other APIs (steps, heart rate, etc.) can read data recorded by the iPhone itself.
  • iOS Health permission: prompts on first call. If denied, subsequent calls return an error. Re-request via the app's Settings → Health Permission, or jump to System Settings to manage each category.
  • Data is delayed: Watch sync to iPhone usually takes a few minutes to an hour.
  • Widget memory limits: Health APIs do not show the permission prompt from inside widgets — they only work after authorization has been granted in the main app.