2024 Spatial ComputingHealth & Fitness
WWDC24 · 12 min · Spatial Computing / Health & Fitness
Get started with HealthKit in visionOS
Discover how to use HealthKit to create experiences that take full advantage of the spatial canvas. Learn the capabilities of HealthKit on the platform, find out how to bring an existing iPadOS app to visionOS, and explore the special considerations governing HealthKit during a Guest User session. You’ll also learn ways to use SwiftUI, Swift Charts, and Swift concurrency to craft innovative experiences with HealthKit.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 8 snippets
Check whether health data is available
import HealthKit
if HKHealthStore.isHealthDataAvailable() {
// Configure HealthKit-powered experiences
} else {
// Omit HealthKit experiences
} Request authorization to read or write data
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 Update number of chart points based on chart’s size
// Update number of chart points based on chart’s size
import SwiftUI
import HealthKit
import Charts
struct ChartView: View {
var chartBinCount: Int
var body: some View {
Chart { ...
// Chart body
}
.onGeometryChange(for: Int.self) { proxy in // Observe for changes to the chart’s size
Int(proxy.size.width / 80) // 80 points per chart point
} action: { newValue in
// Update the number of chart points
chartBinCount = newValue
}
}
} Open chart as a new window
// Opens chart as a new window
struct NewChartViewerButton: View {
(\.openWindow) private var openWindow
var body: some View {
Button("Open In New Window", systemImage: "plus.rectangle.on.rectangle") {
openWindow(id: "chart-viewer-window")
}
}
} HealthKit returns a new error if a write is attempted during a Guest User session
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
}
} Request authorization to State of Mind datatype
// Request authorization to State of Mind datatype
@main
struct HKStateOfMindDataSampleApp: App {
var toggleHealthDataAuthorization = false
var healthDataAuthorized: Bool?
var body: some Scene {
WindowGroup {
TabView { ... }
.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
}
...
}
}
}
}
} Save a State of Mind sample from an emoji type
// Saves a State of Mind sample from an emoji type
public func saveSample(date: Date,
association: HKStateOfMind.Association,
healthStore: HKHealthStore,
didError: Binding<Bool>) async -> SaveDetails? {
do {
let sample = createSample(date: date, association: association)
try await healthStore.save(sample)
} catch {
switch error {
case HKError.errorNotPermissibleForGuestUserMode:
// Drop data you generate in a Guest User session.
didError.wrappedValue = true
return SaveDetails(errorString: "Health data is not saved for Guest Users.")
default:
// Existing error handling.
didError.wrappedValue = true
return SaveDetails(errorString: "Health data could not be saved: \(error)")
}
}
... Present an alert with a message using the given details
// Present an alert with a message using the given details
struct EventView: View {
private var showAlert: Bool = false
private var saveDetails: EmojiType.SaveDetails? = nil
var body: some View {
EmojiPicker()
.alert("Unable to Save Health Data",
isPresented: $showAlert,
presenting: saveDetails,
actions: { _ in }, // default OK button
message: { details in
Text(details.errorString)
})
}
} Resources
Related sessions
-
32 min -
14 min -
16 min