Highlight
HealthKit is fully available on visionOS 2. iPad apps run without changes; native apps can leverage spatial computing for adaptive charts, multi-window comparison, and immersive health experiences.
Core Content
Your iPad health app wants to run on Apple Vision Pro, but you’re unsure how much HealthKit support exists and how much code needs changing. visionOS 2 provides the answer: HealthKit read/write, statistics, and notification capabilities are largely consistent with iPadOS, data syncs across devices via iCloud, and authorization configuration is shared. Both app types work—iPad apps compiled against iOS 17 automatically get the Designed for iPad experience with zero changes; native apps using the visionOS SDK can fully leverage the spatial canvas.
The session uses a mood logging app as a case study, demonstrating a three-step path from iPad adaptation to native visionOS: first, make charts adapt to window scaling with onGeometryChange to dynamically adjust data point count; second, support side-by-side multi-window comparison through openWindow; third, use Full Immersive Space for immersive mood logging, automatically exiting immersion back to the workspace after saving. Guest User scenarios have special restrictions—authorized apps can continue reading data but cannot initiate new authorization or write data; HealthKit returns a new error type errorNotPermissibleForGuestUserMode.
Detailed Content
HealthKit capabilities on visionOS
HealthKit on visionOS behaves consistently with iPadOS: reading and writing health data, aggregate statistics, and registering foreground/background data change notifications are all supported (01:52). Users manage per-app authorization in Settings; data syncs across devices via iCloud (02:04). Both app types can use HealthKit: native apps using the visionOS SDK, and iPad apps compiled against iOS 17 (02:16).
Basic usage is the same as on other platforms. First check availability:
import HealthKit
if HKHealthStore.isHealthDataAvailable() {
// Configure HealthKit-powered experiences
} else {
// Omit HealthKit experiences
}
Key points:
isHealthDataAvailable()is the entry point for all HealthKit logic and applies on visionOS too- Availability may differ across devices and OS versions—always check
- This check is especially important if you need to support earlier visionOS versions (02:43)
Then request authorization:
import HealthKitUI
import SwiftUI
func healthDataAccessRequest(
store: HKHealthStore,
shareTypes: Set<HKSampleType>,
readTypes: Set<HKObjectType>? = nil,
trigger: some Equatable,
completion: @escaping (Result<Bool, any Error>) -> Void
) -> some View
Key points:
healthDataAccessRequestis a SwiftUI view modifier that triggers the system authorization dialogshareTypesare data types you want to write;readTypesare types you want to readtriggercontrols when authorization appears—only when the app actually needs data- Request only the types you need, only when you need them (03:03)
Adaptive charts and multi-window
The core advantage of the spatial canvas is freely resizable windows. Use onGeometryChange to dynamically adjust chart data points as window size changes:
import SwiftUI
import HealthKit
import Charts
struct ChartView: View {
@State var chartBinCount: Int
var body: some View {
Chart { ...
// Chart body
}
.onGeometryChange(for: Int.self) { proxy in
Int(proxy.size.width / 80) // 80 points per chart point
} action: { newValue in
chartBinCount = newValue
}
}
}
Key points:
onGeometryChangeis a new SwiftUI API that listens for view size changes- The closure
proxy -> Intcalculates data point count from width—here one data point per 80pt - The
actionclosure receives the new value and updates@State, driving chart redraw - More data points when the window expands, fewer when it shrinks—users always see reasonably dense charts (05:59)
Multi-window comparison needs only one button:
struct NewChartViewerButton: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
Button("Open In New Window", systemImage: "plus.rectangle.on.rectangle") {
openWindow(id: "chart-viewer-window")
}
}
}
Key points:
- The
openWindowenvironment action opens a new window by identifier - Apps already supporting Split View on iPad require almost no extra work for multi-window
- Two chart windows side by side on the spatial canvas is more flexible than Split View (06:33)
Handling Guest User scenarios
Guest User is a visionOS feature allowing others to try the device. HealthKit has three restrictions in this scenario: authorized apps can continue reading data; new authorization requests cannot be initiated and the system returns an error; data cannot be written, preventing guest data from mixing into the owner’s health library (08:13).
When writes fail, HealthKit returns a new error type:
let sample = HKStateOfMind(date: date, kind: .momentaryEmotion, valence: valence,
labels: [label], associations: [association])
do {
try await healthStore.save(sample)
} catch {
switch error {
case HKError.errorNotPermissibleForGuestUserMode:
// Drop data generated in a Guest User session
default:
// Existing error handling
}
}
Key points:
HKError.errorNotPermissibleForGuestUserModeis a new error type on visionOS- When receiving this error, discard Guest-generated data to avoid accidentally writing to the owner’s health library later
- Existing error handling logic goes in the
defaultbranch unchanged (09:00)
Authorization requests also fail in Guest sessions—handle them in the healthDataAccessRequest callback:
.healthDataAccessRequest(store: healthStore,
shareTypes: [.stateOfMindType()],
readTypes: [.stateOfMindType()],
trigger: toggleHealthDataAuthorization) { result in
switch result {
case .success: healthDataAuthorized = true
case .failure(let error as HKError):
switch (error.code) {
case .errorNotPermissibleForGuestUserMode:
// Defer requests for a later time
default:
// Existing error handling
}
}
}
Key points:
- The system does not show an authorization dialog in Guest sessions—the call fails directly
- Apple recommends deferring authorization requests until a non-Guest session
- Reading data is unaffected—Guest inherits permissions already granted to the owner (09:26)
Finally, give the user a prompt:
struct EventView: View {
@State private var showAlert: Bool = false
@State private var saveDetails: EmojiType.SaveDetails? = nil
var body: some View {
EmojiPicker()
.alert("Unable to Save Health Data",
isPresented: $showAlert,
presenting: saveDetails,
actions: { _ in },
message: { details in
Text(details.errorString)
})
}
}
Key points:
- After a Guest write failure, show an alert informing the user data was not saved
- Guests can experience app functionality without polluting the owner’s data
- The alert uses the
presentingparameter to pass error details and display dynamic error information (09:58)
Core Takeaways
1. Use onGeometryChange for responsive density in health data charts
- Why it’s worth it: On the spatial canvas, window size is user-determined—a fixed data point count makes charts either too sparse or too dense, hurting the experience.
- How to start: Add
.onGeometryChangeto Chart views, calculate data point count asproxy.size.width / constant, and update@Stateto drive redraw.
2. Open multiple windows for comparison scenarios with openWindow(id:) in one line
- Why it’s worth it: Health data often needs comparison (this week vs last week, heart rate vs steps)—two independent windows are more intuitive than tab switching in one window.
- How to start: On iPad apps already supporting Split View, add
@Environment(\.openWindow)and open new windows by identifier; reference Apple’s sample project “Bringing multiple windows to your SwiftUI app.”
3. All HealthKit write paths must handle errorNotPermissibleForGuestUserMode
- Why it’s worth it: Guest User writes fail silently with this error—without handling, you risk data loss or accidentally writing to the owner’s health library.
- How to start: Add a
case .errorNotPermissibleForGuestUserModein both thehealthStore.save()catch branch and thehealthDataAccessRequestfailure callback—discard data and show an alert.
Related Sessions
- Explore wellbeing APIs in HealthKit — Detailed introduction to the State of Mind API used in this session
- Elevate your windowed app for spatial computing — Design guide for adapting existing windowed apps to visionOS
- Create custom environments for your immersive apps in visionOS — Creating custom environments for immersive health experiences
- Bringing multiple windows to your SwiftUI app — Complete implementation guide for SwiftUI multi-window support
Comments
GitHub Issues · utterances