HealthKit is one of the most powerful but most rejection-prone iOS frameworks. Done right, it gives you access to a rich data set users actually trust. Done wrong, your app gets rejected at App Store Review for vague usage descriptions or unhandled permission states. This is how we wire HealthKit into a SwiftUI app at Schedars in 2026.

TL;DR

Five steps: (1) enable the HealthKit capability and Info.plist usage descriptions; (2) request authorization for the specific data types you need; (3) read with HKSampleQuery (one-shot) or HKObserverQuery (live updates); (4) write with HKHealthStore.save; (5) handle revoked permission gracefully with an in-app empty state. Apple does NOT tell you when permission is denied — your code must assume it is and degrade gracefully.

Step 1: Set up entitlements + Info.plist

In Xcode, target → Signing & Capabilities → + Capability → HealthKit. This generates an entitlements file and adds the HealthKit framework. Then in Info.plist add two keys: NSHealthShareUsageDescription (for read access) and NSHealthUpdateUsageDescription (for write access). Both strings show up verbatim in the system permission prompt — write them as if Apple’s reviewer is reading them, because they are.

Bad: "We use HealthKit." Reviewer rejects. Good: "Acme Run logs your distance and active calories so you can track weekly mileage and pace." Specific verbs, specific data types, specific user benefit. Apple’s App Review rejects vague usage descriptions every time.

Step 2: Request authorization

Use HKHealthStore.requestAuthorization(toShare:read:) with two sets — types you want to write (toShare) and types you want to read. You request once at the right moment in the user journey, not on app launch. Right moment = when the user is about to use a feature that needs the data, with a "why" screen explaining the benefit before the system prompt fires.

Important: HealthKit read authorization status is intentionally opaque. You cannot programmatically know if the user denied read access — you’ll just get empty results. Plan your UX assuming "no data" might mean "permission denied" and provide a "Re-enable in Settings" link for users to fix it.

Step 3: Read sample data

Use HKSampleQuery for a one-shot query of historical data, or HKObserverQuery + HKAnchoredObjectQuery for live updates as new data lands. For a dashboard showing today’s steps, you typically combine: a HKStatisticsQuery for the daily total + HKObserverQuery to refresh when new samples arrive while the app is open.

Common mistake: querying on the main thread. HKSampleQuery completes asynchronously on a background queue — dispatch to MainActor before updating SwiftUI state. With Swift Concurrency in 2026, wrap HealthKit completion-handler APIs in async/await to keep the call site clean.

Step 4: Write samples

To write a sample (e.g. a workout the user just completed), construct an HKWorkoutBuilder, add metadata (start/end dates, total energy burned, total distance), call finishWorkout. For non-workout samples (e.g. body weight, hydration), use HKQuantitySample.save. Always check writeStatus — if user denied write access, you’ll get an authorization error at save time, not at request time.

Step 5: Handle revoked permission gracefully

Users can revoke HealthKit permission in Settings → Health → Data Access & Devices at any time. Your app gets no callback. Build empty-state UX: if a query returns zero results when you expect non-zero, show a "Connect Health" button that deep-links to your authorization screen, which itself links to Settings if authorization is already denied.

Sample empty state copy: "No workouts found. If you’ve recorded workouts in Apple Health, you may need to grant permission. [Connect Health]." The button retriggers HKHealthStore.requestAuthorization which silently no-ops if already denied — so additionally provide a "Open Settings" path.

App Store Review gotchas

  • Vague usage descriptions: rejected. Be specific about data types and user benefit.
  • Reading sensitive types (heart rate, blood glucose) without obvious user-facing feature: rejected.
  • Background HealthKit queries without HKBackgroundDelivery configured: silently fails after app close.
  • Asking for too many data types at once: high rejection rate. Request the minimum set; ask for more later as features unlock.
  • No fallback when permission denied: rejected for "incomplete user experience".
  • Writing to types you don’t actually need to write to: rejected for over-broad permission ask.

What we ship at Schedars

We’ve shipped 3 HealthKit-integrated apps in the last 18 months: a workout tracker, a hydration coach, and a sleep insights app. All three passed App Store Review on first submission. The pattern that worked: explicit usage descriptions, contextual permission requests (not on-launch), graceful empty states, and an in-app "Connect Health" path that handles every permission state.

Bottom line

HealthKit done right is one of the most powerful iOS integrations. Done lazily, it’s an App Store Review trap. The discipline is: clear usage descriptions, contextual permission requests, gracious handling of denied/revoked states, and minimal data type asks. Five steps, two days of work, far fewer headaches than retrofitting later.

Building a HealthKit-integrated iOS app and want a review of the integration plan? Send us the data types and user flows — we’ll review within a day.