---
title: "How to Integrate Apple HealthKit (2026)"
canonical: "https://aifitnessapi.com/integrate/healthkit"
cluster: "Integration Guides"
primary_query: "how to integrate Apple HealthKit"
last_reviewed: "2026-07-09"
description: "Integrate Apple HealthKit in Swift: add the capability and Info.plist keys, request authorization, read steps, and get background updates on-device."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"How to Integrate Apple HealthKit (2026)\", AIFitnessAPI, https://aifitnessapi.com/integrate/healthkit"
---

# How to Integrate Apple HealthKit (2026)

> Apple HealthKit gives your iOS app read and write access to the user's on-device health and fitness data, including steps, workouts, and heart rate, through a local store called HKHealthStore. It is not an OAuth cloud API and has no tokens or server endpoints: the user grants access in a system permission sheet and your Swift code reads the data directly on the device. To integrate it you add the HealthKit capability plus Info.plist usage-description keys, call requestAuthorization(toShare:read:), then query data with classes like HKStatisticsQuery. One critical caveat is that HealthKit never tells you whether read access was granted, so you must run the query and treat an empty result as no data or no permission.

- Canonical: https://aifitnessapi.com/integrate/healthkit
- Last reviewed: 2026-07-09
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "How to Integrate Apple HealthKit (2026)", AIFitnessAPI, https://aifitnessapi.com/integrate/healthkit

---

HealthKit is free (no API fees) and iOS/iPadOS/watchOS/visionOS only — there is no cross-platform endpoint. If you also need Android, you integrate [Google Health Connect](/integrate/google-health-connect) separately; the two are compared side by side in [Apple HealthKit vs. Google Health Connect](/fitness-apis/apple-healthkit-vs-google-health-connect). Everything below is native iOS/Swift.

> **Version note.** API signatures below are long-stable public HealthKit API, but a few initializers are version-gated (for example `HKQuantityType(.stepCount)` is iOS 16+). As of 2026, verify exact signatures against the current Apple docs before shipping.

## What you'll need

- An **Apple Developer account** and Xcode. HealthKit requires a paid membership to enable the capability and ship to devices.
- A **physical device or a simulator with the Health app.** HealthKit data is not available on every target (notably some iPad configurations), so you must guard against it at runtime.
- The **HealthKit capability** added to your app target, plus two Info.plist usage-description strings (below). Missing these is the most common first-run crash.

There is no app-registration or partner-approval step like a cloud fitness API — authorization happens entirely on the user's device. The only external gate is App Review, which checks that your usage-description strings are honest and that you actually use the data you request.

## Step 1: Add the HealthKit capability and Info.plist keys

In Xcode, select your target, open **Signing & Capabilities**, and add the **HealthKit** capability. This provisions the entitlement your app needs to talk to `HKHealthStore`.

Then add usage-description strings to your Info.plist. These are the text the system shows the user in the permission sheet, and they are mandatory:

- **`NSHealthShareUsageDescription`** — required if you **read** any health data.
- **`NSHealthUpdateUsageDescription`** — required if you **write / share** any health data.

```xml
<!-- Info.plist -->
<key>NSHealthShareUsageDescription</key>
<string>We read your step count to show your daily activity progress.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>We save completed workouts to your Health app.</string>
```

If you call the authorization API without the matching key present, the app **crashes** rather than showing a sheet — so add both keys for whichever operations you support before writing any code.

## Step 2: Check availability and request authorization

Everything starts with a single `HKHealthStore` instance. Before touching it, guard with the static **`HKHealthStore.isHealthDataAvailable()`**, which returns false on unsupported devices. Then call **`requestAuthorization(toShare:read:)`** with the set of types you want to write (`Set` of `HKSampleType`) and the set you want to read (`Set` of `HKObjectType`).

```swift
import HealthKit

let healthStore = HKHealthStore()

func requestAuth() async throws {
    guard HKHealthStore.isHealthDataAvailable() else { return }

    let stepType = HKQuantityType(.stepCount)          // iOS 16+ initializer
    let toRead: Set<HKObjectType> = [stepType]
    let toShare: Set<HKSampleType> = [stepType]

    // Presents the system permission sheet.
    try await healthStore.requestAuthorization(toShare: toShare, read: toRead)
}
```

The `async` form shown here is iOS 15+. Older code uses the completion-handler variant `requestAuthorization(toShare:read:completion:)`; either is fine. On older OS versions you can also build the step type with `HKObjectType.quantityType(forIdentifier: .stepCount)` instead of the `HKQuantityType(.stepCount)` initializer.

## Step 3: Understand the read-denial caveat (important)

This is the single most misunderstood part of HealthKit, so handle it deliberately. To avoid leaking whether a person has health data at all, **the system does not tell you whether the user granted or denied READ access.** `authorizationStatus(for:)` reliably reflects only **write / share** status; for read types it typically returns `.notDetermined` even after the user grants access.

The consequence: `requestAuthorization` succeeding means "the sheet was shown," **not** "read was granted." Never gate your UI on read-authorization status. The documented pattern is to **just run the query and treat an empty result as "no data or no permission"** — the two are indistinguishable, and that is by design. Build your UI to degrade gracefully when a query comes back empty.

## Step 4: Read today's step count

For a running total like steps, use **`HKStatisticsQuery`** with the `.cumulativeSum` option (rather than summing raw samples yourself). Scope it with a date predicate so you only aggregate today's data:

```swift
func readTodaySteps() {
    let stepType = HKQuantityType(.stepCount)
    let startOfDay = Calendar.current.startOfDay(for: Date())
    let predicate = HKQuery.predicateForSamples(
        withStart: startOfDay, end: Date(), options: .strictStartDate)

    let query = HKStatisticsQuery(
        quantityType: stepType,
        quantitySamplePredicate: predicate,
        options: .cumulativeSum
    ) { _, statistics, error in
        guard let sum = statistics?.sumQuantity() else {
            // Empty == no data OR read permission not granted (indistinguishable).
            return
        }
        let steps = sum.doubleValue(for: HKUnit.count())
        print("Steps today: \(steps)")
    }
    healthStore.execute(query)
}
```

If you need the individual raw samples instead of an aggregate — for example to inspect timestamps or the source app — use **`HKSampleQuery`** with the same predicate. For per-workout rep and form data that HealthKit does not capture, pair this with an on-device motion pipeline; the [AI workout tracking on iOS with Swift guide](/guides/ai-workout-tracking-ios-swift) walks through that side.

## Step 5: Get background updates with HKObserverQuery

Polling drains battery. To react when new data lands, register an **`HKObserverQuery`** and turn on **`enableBackgroundDelivery(for:frequency:withCompletion:)`**. The observer's completion handler **must** be called or the OS stops delivering updates.

```swift
func startObservingSteps() {
    let stepType = HKQuantityType(.stepCount)

    let observer = HKObserverQuery(sampleType: stepType, predicate: nil) {
        _, completionHandler, error in
        // Fetch new data here (e.g. an HKAnchoredObjectQuery), then:
        completionHandler()   // MUST call, or the OS stops delivering updates.
    }
    healthStore.execute(observer)

    healthStore.enableBackgroundDelivery(
        for: stepType, frequency: .immediate) { success, error in
        // .immediate / .hourly / .daily via HKUpdateFrequency; delivery is OS-throttled.
    }
}
```

Frequency is chosen from `HKUpdateFrequency` (`.immediate`, `.hourly`, `.daily`). Note that background delivery is **budgeted and throttled** by the OS — you will not get literal real-time firing, and the actual cadence depends on system conditions. Design around eventual delivery, not guaranteed timing.

## Gotchas and production notes

- **Read denial is invisible.** Repeating the Step 3 point because it bites everyone: never assume an empty query means "no data." Always handle empty results as a valid, expected state.
- **Both Info.plist keys or a crash.** Add `NSHealthShareUsageDescription` for reads and `NSHealthUpdateUsageDescription` for writes before your first authorization call.
- **Availability first.** Guard every entry point with `HKHealthStore.isHealthDataAvailable()`; it is false on unsupported devices and your queries will otherwise fail silently.
- **Aggregate, don't hand-sum, for cumulative types.** `HKStatisticsQuery` with `.cumulativeSum` avoids double-counting when multiple sources (phone plus watch) write steps.
- **App Review scrutinizes health usage.** Your usage-description strings must accurately describe what you do with the data, and you should request only the types you genuinely use.
- **Background delivery is throttled.** Treat `HKObserverQuery` plus `enableBackgroundDelivery` as best-effort, and always call the completion handler.
- **Version-gate the volatile bits.** `HKQuantityType(.stepCount)` is iOS 16+; fall back to `HKObjectType.quantityType(forIdentifier:)` on older targets, and verify current signatures in Apple's docs before release.

## FAQ

### Does Apple HealthKit use OAuth or API keys?

No. HealthKit is an on-device, permission-based framework, not a cloud API. There are no client secrets, access tokens, refresh flows, or server endpoints. The user grants access in a native system permission sheet and your app reads or writes a local, OS-managed store directly. The only credential you need is a paid Apple Developer account to enable the capability and ship the app.

[Permalink](https://aifitnessapi.com/integrate/healthkit#faq-1)

### Why can't I tell whether the user granted read permission?

This is intentional. To avoid leaking whether a person has any health data, HealthKit does not report read-authorization status. authorizationStatus(for:) reliably reflects only write/share access; for read types it typically returns notDetermined even after a grant. The documented pattern is to run your query and treat an empty result as no data or no permission, since the two are indistinguishable by design.

[Permalink](https://aifitnessapi.com/integrate/healthkit#faq-2)

### Does HealthKit work on Android or on the web?

No. HealthKit is limited to Apple platforms (iOS, iPadOS, watchOS, visionOS) and there is no cross-platform server endpoint. For Android you integrate Google Health Connect, which is a separate on-device framework with its own SDK, permissions model, and quirks such as a default 30-day history window.

[Permalink](https://aifitnessapi.com/integrate/healthkit#faq-3)

### Why does my app crash the first time I request authorization?

Almost always a missing Info.plist usage-description key. HealthKit requires NSHealthShareUsageDescription to read data and NSHealthUpdateUsageDescription to write data. If you call requestAuthorization without the key that matches your operation, the app crashes instead of showing the permission sheet, so add both keys for whatever you support before running the flow.

[Permalink](https://aifitnessapi.com/integrate/healthkit#faq-4)

### How should I read step totals without double-counting?

Use HKStatisticsQuery with the cumulativeSum option rather than fetching raw samples and adding them yourself. When multiple sources such as an iPhone and an Apple Watch both write steps, aggregating through the statistics query avoids double-counting. Verify version-gated details like the HKQuantityType(.stepCount) initializer, which is iOS 16+, against current Apple docs.

[Permalink](https://aifitnessapi.com/integrate/healthkit#faq-5)
