Skip to content
AF
Integration Guides

How to Integrate Apple HealthKit (2026)

Last verified October 2, 2026 · 10 min read

Sources checked (23) · October 2, 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 HKHealthStore, which Apple calls the access point for all data managed by HealthKit. It is not an OAuth cloud API: there are no tokens or server endpoints in Apple's setup, 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.

Covered here:HealthKitHealth Connect

HealthKit data lives only on Apple devices — iPhone, Apple Watch, Vision Pro, and iPads on iPadOS 17 or later; Apple notes the framework also compiles for macOS and older iPadOS, but there isHealthDataAvailable() returns false — and there is no cross-platform endpoint. If you also need Android, you integrate Google Health Connect separately; the two are compared side by side in Apple HealthKit vs. Google Health Connect. HealthKit's App Review gate, and every page on this site that covers HealthKit, are collected in the HealthKit API overview. Everything below is native iOS/Swift. If your app is React Native or Flutter instead, react-native-health vs @kingstinct/react-native-healthkit compares the two React Native bridges to HealthKit, and health_kit_reporter covers an iOS-only Flutter plugin.

Version note. API signatures below are long-stable public HealthKit API, but a few initializers are version-gated (for example HKQuantityType(.stepCount) is iOS 15+ and watchOS 8+). Signatures and availability were checked against Apple's documentation on October 2, 2026; re-check them before shipping.

What you'll need#

  • An Apple Developer account and Xcode. Apple's capability table lists HealthKit for free Apple Developer accounts as well as paid memberships; distributing on the App Store needs the paid Apple Developer Program.
  • A device with a HealthKit store — iPhone, Apple Watch, Vision Pro, or an iPad on iPadOS 17 or later. HealthKit data isn't available on iPadOS 16 and earlier or on macOS, so guard against it at runtime, and test background delivery on a device: Apple says background queries aren't supported in the Simulator.
  • The HealthKit capability added to your app target, plus two Info.plist usage-description strings (below). Missing the one your request needs crashes the app at the authorization call.

There is no app-registration or partner-approval step like a cloud fitness API — authorization happens on the user's device. The external gate is App Review: Apple calls the purpose strings an App Store requirement for any HealthKit app, App Review Guideline 5.1.1 asks that they clearly and completely describe your use of the data and that you request only data your core features need, and Apple warns it may reject apps that enable the Clinical Health Records capability without actually using health records.

The strings you will paste#

Every key and call in this table is copied from Apple's developer documentation, fetched on October 2, 2026. The quoted column is Apple's own wording, and the name in parentheses is the title Apple's documentation gives the item in Xcode.

StringKind (Xcode name)Apple's description, verbatimApple reference
com.apple.developer.healthkitEntitlement ("HealthKit")"A Boolean value that indicates whether the app may request user authorization to access health and activity data that appears in the Health app."HealthKit Entitlement
NSHealthShareUsageDescriptionInfo.plist key ("Privacy - Health Share Usage Description")"A message that explains to people why the app requests permission to read samples from the HealthKit store."NSHealthShareUsageDescription
NSHealthUpdateUsageDescriptionInfo.plist key ("Privacy - Health Update Usage Description")"A message to the user that explains why the app requested permission to save samples to the HealthKit store."NSHealthUpdateUsageDescription
com.apple.developer.healthkit.background-deliveryEntitlement ("HealthKit Observer Query Background Delivery")"A Boolean value that indicates whether observer queries receive updates while running in the background."com.apple.developer.healthkit.background-delivery
requestAuthorization(toShare:read:)HKHealthStore method, async, iOS 15+"Asynchronously requests permission to save and read the specified data types."requestAuthorization(toShare:read:)
requestAuthorization(toShare:read:completion:)HKHealthStore method, completion handler, iOS 8+"Requests permission to save and read the specified data types."requestAuthorization(toShare:read:completion:)

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

In Xcode, select your target, open Signing & Capabilities, and add the HealthKit capability. That is how the com.apple.developer.healthkit entitlement gets into your app; Apple's entitlement page says "To add this entitlement to your app, enable the HealthKit capability in Xcode."

Then add the two usage-description keys from the table: NSHealthShareUsageDescription if you read any health data, NSHealthUpdateUsageDescription if you write / share any. Their values are the text the system shows the user in the permission sheet. For projects created with Xcode 13 or later, Apple says to set these keys in the Target Properties list on the app's Info tab; projects created with Xcode 12 or earlier set them in the Info.plist file:

<!-- 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>

Apple's requestAuthorization reference is blunt about skipping this step: "You must set the usage keys, or your app will crash when you request authorization." Add the key for each operation 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).

import HealthKit

let healthStore = HKHealthStore()

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

    let stepType = HKQuantityType(.stepCount)          // iOS 15+ 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. For targets older than iOS 15 you can build the step type with HKObjectType.quantityType(forIdentifier: .stepCount) instead of the HKQuantityType(.stepCount) initializer — but Apple's reference marks that class method for deprecation in iOS 27.2, so use it only where you have to.

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:) covers only the share (write) side — Apple: "This method checks the authorization status for saving data" — and tells you nothing about reads.

The consequence: requestAuthorization succeeding means the request was processed — Apple says the success value "doesn't indicate whether the user actually granted permission" — not that read was granted. Never gate your UI on read-authorization status. The documented behavior is that a denied read "simply appears as if there is no data of the requested type" (your own app's samples stay visible), so run the query and treat an empty result as "no data or no permission" — the two are indistinguishable by design. Build your UI to degrade gracefully when a query comes back empty.

Apple's authorization flow now adds a third case: a person can grant full access, access limited to a recent window of their history, or none. From iOS 27, earliestAuthorizedSampleDate(for:) returns the earliest readable date for each type under such a limit — the only authorization state Apple lets you identify — so start date-range queries no earlier than that date, and treat anything before it as unknown rather than 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). Apple documents stepCount samples as using count units and measuring cumulative values, which is why the sum is the right statistic and the result is read in HKUnit.count(). Not every type is cumulative, so check a type's aggregation style in HealthKit data types by group before you pick an option. Scope the query with a date predicate so you only aggregate today's data:

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 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:). Call the observer's completion handler when you finish processing: Apple says that if you don't, HealthKit keeps retrying with a backoff and, after your app fails to respond three times, stops sending background updates. Apple also says to set up observer queries in application(_:didFinishLaunchingWithOptions:), so they exist before HealthKit delivers updates at launch.

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()   // Call it, or HealthKit backs off and eventually stops.
    }
    healthStore.execute(observer)

    healthStore.enableBackgroundDelivery(
        for: stepType, frequency: .immediate) { success, error in
        // HKUpdateFrequency: .immediate, .hourly, .daily or .weekly — a maximum, not a schedule.
    }
}

Frequency is chosen from HKUpdateFrequency (.immediate, .hourly, .daily, .weekly), and it is a ceiling: Apple says the system wakes your app at most once per period, some sample types are capped at hourly, and on watchOS background updates share a budget with background app refresh tasks — four an hour, as long as the app has a complication on the active watch face. Design around eventual delivery, not guaranteed timing. If updates never arrive at all, work through HealthKit background delivery not working.

Two documented constraints sit on top of that. Apple's enableBackgroundDelivery reference says that for iOS 15 and watchOS 8 and later you must add the com.apple.developer.healthkit.background-delivery entitlement from the table above; without it, the call fails with an HKError.Code.errorAuthorizationDenied error. The same page notes that on iOS, stepCount samples have an hourly maximum frequency, so .immediate on steps still wakes your app at most once an hour.

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(); where it is false, Apple says other HealthKit methods fail with errorHealthDataUnavailable (or errorHealthDataRestricted where HealthKit is restricted, for example in an enterprise environment).
  • Aggregate, don't hand-sum, for cumulative types. Apple says statistics queries merge the data from all of your sources before calculating, unless you ask for .separateBySource; summing raw samples yourself counts overlapping iPhone and Apple Watch samples twice.
  • App Review scrutinizes health usage. Guideline 5.1.1 wants purpose strings that clearly and completely describe your use of the data, and access only to data relevant to your core functionality.
  • Background delivery is throttled and entitled. Treat HKObserverQuery plus enableBackgroundDelivery as best-effort, add the background-delivery entitlement, and always call the completion handler.
  • Version-gate the volatile bits. HKQuantityType(.stepCount) is iOS 15+; fall back to HKObjectType.quantityType(forIdentifier:) only on older targets (Apple marks it for deprecation in iOS 27.2), and verify current signatures in Apple's docs before release.

Frequently asked questions

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. What you need is an Apple Developer account to add the capability — Apple lists HealthKit for free accounts as well as paid ones — and the paid Apple Developer Program to distribute on the App Store.
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:) checks only whether your app may save a type, and a denied read simply looks like no data (apart from samples your own app wrote). Run your query and treat an empty result as no data or no permission. From iOS 27 the one read state you can detect is a limited history window, through earliestAuthorizedSampleDate(for:).
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.
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.
How should I read step totals without double-counting?
Use HKStatisticsQuery with the cumulativeSum option rather than fetching raw samples and adding them yourself. Apple says statistics queries merge the data from all of your sources before calculating, so an iPhone and an Apple Watch that both record steps are not simply added together. Verify version-gated details like the HKQuantityType(.stepCount) initializer, which is iOS 15+, against current Apple docs.

All 113 questions in Integration Guides →

Keep reading

From the blog

Findings counted out of this site’s own datasets.

Elsewhere on the site

Pages that share this one’s concepts and sources, from other sections.

Next steps

Was this page useful?

Independent comparison, last reviewed October 2, 2026. Pricing, rate limits, and feature availability change often — confirm current details in each provider’s official documentation before you commit. Product and company names are trademarks of their respective owners; AIFitnessAPI is not affiliated with, endorsed by, or sponsored by any product listed here.

← All integration guides · by AIFitnessAPI