HealthKit Integration for iOS in 2026: Data Types, Permissions, and Production Patterns
HealthKit integrations need precise permissions, efficient queries, local persistence, and privacy-safe AI boundaries to survive App Store review and production use.
HealthKit is one of the most privacy-sensitive APIs on the Apple platform. It exposes heart rate, sleep analysis, step counts, blood glucose, menstrual cycle data, and dozens of other types that sit at the intersection of personal identity and medical history. Getting the integration right matters — not just for App Store approval, but for the trust your product depends on.
This article covers the data types you will actually use, how the permission model works, the production patterns that hold up under real conditions, and the architectural decisions that separate a compliant health app from one that fails review or leaks data.
How HealthKit's Permission Model Works
HealthKit uses a per-type, per-direction permission model. Read and write permissions are requested separately. A user can grant write access to step count while denying read access to the same type. Your app never learns whether a specific type was denied — HKHealthStore returns an empty result set, not an error. This is intentional. It prevents apps from inferring sensitive states from permission denials.
The practical consequence: you cannot branch your UI on whether a specific type was authorized. Design for the empty-result case as a first-class state, not an edge case.
Checking HealthKit Availability
Not all devices support HealthKit. iPads do not. Call HKHealthStore.isHealthDataAvailable() before any other HealthKit code. If it returns false, disable the entire integration path — not hide it behind a loading spinner.
guard HKHealthStore.isHealthDataAvailable() else {
// Disable HealthKit features entirely — not an iPhone
return
}
Requesting Authorization
Authorization requests must be triggered by a user action, not on app launch. Apple's review team will reject an app that presents the HealthKit permission sheet without a clear user-initiated context.
let typesToRead: Set<HKObjectType> = [
HKObjectType.quantityType(forIdentifier: .heartRate)!,
HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
]
let typesToWrite: Set<HKSampleType> = [
HKObjectType.quantityType(forIdentifier: .stepCount)!
]
try await healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead)
Request only the types your app uses in the current session context. Requesting 20 types upfront because you might need them later is a common rejection trigger. Apple's review guidelines are explicit: request the minimum set required for the feature the user is about to use.
Data Types Worth Knowing in 2026
HealthKit organizes data into four main object types: HKQuantityType, HKCategoryType, HKCorrelationType, and HKWorkoutType. A fifth — HKClinicalType — covers FHIR-based clinical records and requires additional entitlements.
Quantity Types
These represent measurable values with units. Common ones:
.heartRate— measured incount/min.stepCount— cumulative, measured incount.activeEnergyBurned— measured inkcal.bloodGlucose— measured inmg/dLormmol/L.oxygenSaturation— measured as a percentage.bodyMass— measured inkgorlb.restingHeartRate— distinct from.heartRate; Apple Watch writes this separately
Units matter. HKUnit is strict — passing the wrong unit to a quantity type throws at runtime. Define your units as constants, not inline strings.
Category Types
These represent states rather than measurements. Sleep analysis is the most common:
.sleepAnalysis— values areHKCategoryValueSleepAnalysisenum cases:.inBed,.asleep,.awake, and the more granular.asleepCore,.asleepDeep,.asleepREMadded in later OS versions
Menstrual cycle tracking, mood, and symptoms also live here. Under HIPAA or similar frameworks, these types require careful handling — they are sensitive by definition.
Correlation Types
.bloodPressure is the primary one. It wraps systolic and diastolic quantity samples together. Writing blood pressure as two separate samples will not display correctly in the Health app — use HKCorrelation.
Workout Types
HKWorkout and HKWorkoutRoute handle structured exercise sessions. HKWorkoutRoute stores GPS data as a CLLocation series and requires the com.apple.developer.healthkit.background-delivery entitlement for background writes.
Querying HealthKit Efficiently
The two query types you will use most are HKSampleQuery and HKStatisticsCollectionQuery.
HKSampleQuery
Returns individual samples within a predicate. Use this when you need raw data points — individual heart rate readings during a workout, for example.
let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)!
let predicate = HKQuery.predicateForSamples(
withStart: startDate,
end: endDate,
options: .strictStartDate
)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(
sampleType: heartRateType,
predicate: predicate,
limit: HKObjectQueryNoLimit,
sortDescriptors: [sortDescriptor]
) { _, samples, error in
guard let samples = samples as? [HKQuantitySample], error == nil else { return }
// process samples
}
healthStore.execute(query)
HKStatisticsCollectionQuery
Returns aggregated statistics over time intervals — daily step totals, weekly active energy, and so on. This is the right query for dashboards and trend views.
let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let anchorDate = Calendar.current.startOfDay(for: Date())
let interval = DateComponents(day: 1)
let query = HKStatisticsCollectionQuery(
quantityType: stepType,
quantitySamplePredicate: nil,
options: .cumulativeSum,
anchorDate: anchorDate,
intervalComponents: interval
)
query.initialResultsHandler = { _, results, error in
guard let results = results, error == nil else { return }
results.enumerateStatistics(from: startDate, to: endDate) { statistics, _ in
let steps = statistics.sumQuantity()?.doubleValue(for: .count()) ?? 0
// use steps
}
}
healthStore.execute(query)
Do not run HKSampleQuery in a loop to simulate aggregation. It is slower and produces inconsistent results across time zones. Use HKStatisticsCollectionQuery for anything aggregated.
Background Delivery
HealthKit can deliver updates to your app in the background when new data is written by another app or device. This requires the com.apple.developer.healthkit.background-delivery entitlement and explicit observer query setup.
Background delivery does not wake your app on every new sample — it coalesces updates and delivers them periodically. The frequency is controlled by HKUpdateFrequency: .immediate, .hourly, .daily, .weekly. .immediate does not mean real-time. It means as soon as the system determines delivery is appropriate.
let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)!
let observerQuery = HKObserverQuery(sampleType: heartRateType, predicate: nil) { _, completionHandler, error in
guard error == nil else {
completionHandler()
return
}
// fetch new samples, then call completionHandler
completionHandler()
}
healthStore.execute(observerQuery)
healthStore.enableBackgroundDelivery(for: heartRateType, frequency: .immediate) { success, error in
// handle registration result
}
Always call completionHandler() — even on error. Failing to call it causes the system to stop delivering background updates.
Production Patterns That Matter
Store HealthKit Data Locally
Do not treat HealthKit as your primary data store. Query it, process the results, and persist what your app needs in Core Data or SwiftData. This gives you offline access, faster reads, and control over the data model. HealthKit queries are not free — they involve cross-process communication and disk I/O.
A local-first architecture where HealthKit is a data source rather than a database is the correct pattern for any production health app. This is the same architectural principle behind the CalmLedger case study at 3Nsofts — data processed on-device, persisted locally, never sent to a server.
Handle Unit Mismatches Explicitly
HealthKit will not coerce units. If your UI displays blood glucose in mmol/L and the stored sample uses mg/dL, you get the wrong number without an error. Define a unit preference at the app level and convert at the query boundary — not in the view layer.
Anchor Queries for Incremental Sync
When syncing HealthKit data to a local store, use HKAnchoredObjectQuery instead of date-range queries. Anchored queries return only the samples added or deleted since the last sync point — no re-processing data you have already stored, and background sync stays efficient.
var anchor: HKQueryAnchor? = loadAnchorFromDisk()
let anchoredQuery = HKAnchoredObjectQuery(
type: stepType,
predicate: nil,
anchor: anchor,
limit: HKObjectQueryNoLimit
) { _, newSamples, deletedSamples, newAnchor, error in
guard error == nil else { return }
// process newSamples and deletedSamples
anchor = newAnchor
saveAnchorToDisk(newAnchor)
}
healthStore.execute(anchoredQuery)
Persist the anchor between sessions. Lose it and you re-query everything from the beginning.
Privacy Strings That Pass Review
The NSHealthShareUsageDescription and NSHealthUpdateUsageDescription keys in Info.plist must describe specifically what data your app reads and writes, and why. Generic strings like "Used for health features" will fail review.
Write them as complete sentences that name the data type and the feature it enables:
"This app reads your heart rate data to calculate recovery scores in the Training tab."
One sentence per purpose. No marketing language. Apple's review team reads these.
HealthKit and On-Device AI
Health data is a natural input for on-device inference. Sleep patterns, heart rate variability, and activity data can feed Core ML models for anomaly detection, trend prediction, or personalized recommendations — without any of that data leaving the device.
The architecture is straightforward: query HealthKit, transform the samples into the feature vector your model expects, run inference via MLModel, surface the result in the UI. No network call. No cloud dependency. The Core ML 8 integration patterns for production iOS guide covers the inference pipeline in detail, including input normalization for time-series health data.
If you are evaluating whether your existing codebase is ready for this kind of integration, the Core ML integration checklist for 2026 covers the architecture, data pipeline, and App Store compliance checks you need before starting.
For the privacy architecture side — how to structure data flows so health data never touches a server even when AI is involved — the privacy-preserving AI architectures guide is the right reference.
App Store Compliance Checklist
Before submitting a HealthKit app, verify:
NSHealthShareUsageDescriptionandNSHealthUpdateUsageDescriptionare present and specificcom.apple.developer.healthkitentitlement is enabled in the provisioning profileHKHealthStore.isHealthDataAvailable()is checked before any HealthKit call- Authorization is requested only in response to a user action
- Only the types actually used are included in the authorization request
- Background delivery entitlement is present if you use
HKObserverQuery - The app handles the empty-result case for every HealthKit query — not just the error case
HKClinicalTypeusage includes the separatecom.apple.developer.healthkit.clinical-recordsentitlement
Missing any of these is a rejection, not a warning.
Working with Apple Watch
HealthKit data from Apple Watch arrives through the same HKHealthStore API on iPhone — the Watch writes to the shared HealthKit database and your iPhone app reads from it. You do not need a Watch app to consume Watch-generated data.
If you are building a Watch companion app, HealthKit on watchOS has the same authorization model but a separate HKHealthStore instance. Authorization granted on iPhone does not automatically carry to the Watch app. Request authorization independently on each platform.
For real-time heart rate during a workout session, use HKWorkoutSession on watchOS rather than polling HKSampleQuery. The workout session keeps the sensor active and delivers samples through HKLiveWorkoutBuilder.
FAQs
What is the difference between HKQuantityType and HKCategoryType in HealthKit?
HKQuantityType represents measurable values with units — heart rate in beats per minute, step count, blood glucose in mg/dL. HKCategoryType represents discrete states or events — sleep stages, symptoms, menstrual cycle phases. The distinction determines which query type and sample class you use to read and write the data.
Why does HealthKit return empty results instead of an error when permission is denied?
This is a deliberate privacy design. A distinct error for denied permissions would let an app infer sensitive health states from the pattern of denials — inferring a pregnancy from a denied menstrual cycle permission, for example. Empty results prevent that inference attack.
Can I use HealthKit on iPad?
No. HKHealthStore.isHealthDataAvailable() returns false on iPad. HealthKit is only available on iPhone and Apple Watch. Design your app to degrade gracefully on devices where it is unavailable.
What is HKAnchoredObjectQuery and when should I use it?
HKAnchoredObjectQuery returns only the samples added or deleted since a stored anchor point. Use it for incremental sync between HealthKit and your local data store. It avoids re-processing data you have already handled — which makes it more efficient than date-range queries for background sync.
Do I need a separate entitlement for background HealthKit delivery?
Yes. Background delivery requires com.apple.developer.healthkit.background-delivery in addition to the base com.apple.developer.healthkit entitlement. Both must be present in your provisioning profile and your Entitlements.plist.
How should I handle time zone changes in HealthKit data?
HealthKit stores samples with absolute timestamps. When a user travels across time zones, the timestamps remain accurate but your date-bucketing logic may assign samples to the wrong local day. Use Calendar with the user's current time zone for all date calculations, and re-bucket data when the time zone changes — do not assume stored groupings are stable.
Can HealthKit data be used as input to a Core ML model on-device?
Yes. Query HealthKit, transform the samples into the feature vector your model expects, and run inference locally via MLModel. No data leaves the device. This is the correct architecture for health apps that want AI-powered insights without a cloud dependency.