ハイライト
HealthKit は visionOS 2 で完全に利用可能です。iPad アプリは変更なしで動作し、ネイティブアプリは空間コンピューティングでチャートの適応、マルチウィンドウ比較、没入型ヘルス体験を実現できます。
主要内容
iPad ヘルスアプリを Apple Vision Pro で動かしたいが、HealthKit のサポート範囲や必要なコード変更量が分からない。visionOS 2 が答えを示します。HealthKit の読み書き、統計、通知機能は iPadOS とほぼ同等で、データは iCloud でデバイス間同期され、認可設定も共通です。2 つのアプリ形態が使えます。iOS 17 向けにビルドした iPad アプリは Designed for iPad 体験を自動取得し、変更ゼロ。visionOS SDK を使うネイティブアプリは空間キャンバスを最大限活用できます。
Session では感情記録アプリを事例に、iPad 適応からネイティブ visionOS への 3 ステップをデモします。第一に、onGeometryChange でチャートがウィンドウスケールに追従し、データポイント数を動的調整。第二に、openWindow でマルチウィンドウ並列比較をサポート。第三に、Full Immersive Space で没入型感情記録を行い、保存後に自動的に没入状態を終了してワークスペースに戻る。Guest User シナリオには特別な制限があります。認可済みアプリはデータ読み取りを継続できますが、新規認可の開始やデータ書き込みはできません。HealthKit は新しいエラー型 errorNotPermissibleForGuestUserMode を返します。
詳細
visionOS 上の HealthKit 機能
visionOS 上の HealthKit は iPadOS と同様に動作します。ヘルスデータの読み書き、集計統計、フォアグラウンド/バックグラウンドのデータ変更通知登録がすべてサポートされます(01:52)。ユーザーは設定でアプリごとの認可を管理し、データは iCloud でデバイス間同期されます(02:04)。visionOS SDK を使うネイティブアプリと、iOS 17 向けにビルドした iPad アプリの両方が HealthKit を使えます(02:16)。
基本的な使い方は他プラットフォームと同じです。まず利用可能性を確認します:
import HealthKit
if HKHealthStore.isHealthDataAvailable() {
// Configure HealthKit-powered experiences
} else {
// Omit HealthKit experiences
}
キーポイント:
isHealthDataAvailable()はすべての HealthKit ロジックの入口で、visionOS でも同様に適用される- デバイスや OS バージョンによって利用可能性が異なる場合があるため、必ず確認する
- 早期 visionOS バージョンとの互換が必要な場合、この確認は特に重要(02:43)
次に認可をリクエストします:
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
キーポイント:
healthDataAccessRequestは SwiftUI ビュー修飾子で、システム認可ダイアログをトリガーするshareTypesは書き込みたいデータ型、readTypesは読み取りたい型triggerは認可表示のタイミングを制御し、アプリが実際にデータを必要とするときのみ表示する- 必要な型だけ、必要なときだけリクエストする(03:03)
チャート適応とマルチウィンドウ
空間キャンバスの核心優位性は、ウィンドウを自由にリサイズできることです。onGeometryChange でウィンドウサイズに応じてチャートのデータポイントを動的に調整します:
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
}
}
}
キーポイント:
onGeometryChangeは SwiftUI の新 API で、ビューサイズ変更を監視する- クロージャ
proxy -> Intで幅からデータポイント数を計算。ここでは 80pt あたり 1 データポイント actionクロージャが新値を受け取り@Stateを更新し、チャート再描画を駆動する- ウィンドウ拡大でデータポイント増、縮小で減。ユーザーは常に適切な密度のチャートを見られる(05:59)
マルチウィンドウ比較はボタン 1 つで済みます:
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")
}
}
}
キーポイント:
openWindow環境アクションでウィンドウ識別子から新ウィンドウを開く- iPad で Split View をすでにサポートしているアプリは、マルチウィンドウへの移行にほぼ追加作業不要
- 空間キャンバス上で 2 つのチャートウィンドウを並べるのは Split View より自由(06:33)
Guest User シナリオの処理
Guest User は visionOS の機能で、他の人がデバイスを試用できます。HealthKit にはこのシナリオで 3 つの制限があります。認可済みアプリはデータ読み取りを継続できる。新規認可リクエストは開始できず、システムはエラーを返す。データ書き込みはできず、ゲストデータが機主のヘルスライブラリに混ざるのを防ぐ(08:13)。
書き込み失敗時、HealthKit は新しいエラー型を返します:
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
}
}
キーポイント:
HKError.errorNotPermissibleForGuestUserModeは visionOS の新エラー型- このエラーを受け取ったら Guest セッションで生成したデータを破棄し、後から機主のヘルスライブラリに誤って書き込まない
- 既存のエラー処理ロジックは
default分岐にそのまま置く(09:00)
Guest セッションでは認可リクエストも失敗する。healthDataAccessRequest のコールバックで処理する:
.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
}
}
}
キーポイント:
- Guest セッションではシステムは認可ダイアログを表示せず、呼び出しは直接失敗する
- Apple は非 Guest セッションまで認可リクエストを延期することを推奨
- データ読み取りは影響を受けない。Guest は機主にすでに付与された権限を継承する(09:26)
最後にユーザーへ通知します:
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)
})
}
}
キーポイント:
- Guest 書き込み失敗後、alert でデータが保存されなかったことを通知
- Guest はアプリ機能を体験できるが、機主のデータを汚染しない
- alert は
presentingパラメータでエラー詳細を渡し、動的にエラー情報を表示(09:58)
重要ポイント
1. ヘルスデータチャートの表示密度には onGeometryChange でレスポンシブ対応
- なぜ価値があるか: 空間キャンバスではウィンドウサイズはユーザー次第。固定データポイント数だとチャートが疎すぎるか密すぎるかになり、体験が悪化する。
- 始め方: Chart ビューに
.onGeometryChangeを追加し、proxy.size.width / 定数でデータポイント数を計算、@State更新で再描画を駆動する。
2. 比較シーンではマルチウィンドウを開き、openWindow(id:) で 1 行実装
- なぜ価値があるか: ヘルスデータは比較が多い(今週 vs 先週、心拍 vs 歩数)。1 ウィンドウの Tab 切り替えより 2 つの独立ウィンドウの方が直感的。
- 始め方: Split View 対応済み iPad アプリに
@Environment(\.openWindow)を追加し、ウィンドウ識別子で新ウィンドウを開く。Apple サンプル「Bringing multiple windows to your SwiftUI app」を参照。
3. すべての HealthKit 書き込みパスで errorNotPermissibleForGuestUserMode を処理
- なぜ価値があるか: Guest User の書き込みはこのエラーで静かに失敗する。未処理だとデータ損失や機主ヘルスライブラリへの誤書き込みのリスクがある。
- 始め方:
healthStore.save()の catch 分岐とhealthDataAccessRequestの failure コールバックにcase .errorNotPermissibleForGuestUserModeを追加し、データ破棄と alert 表示を行う。
関連セッション
- Explore wellbeing APIs in HealthKit — 本 Session で使う State of Mind API の詳細紹介
- Elevate your windowed app for spatial computing — 既存ウィンドウアプリを visionOS に適応するデザインガイド
- Create custom environments for your immersive apps in visionOS — 没入型ヘルス体験向けカスタム環境の作成
- Bringing multiple windows to your SwiftUI app — SwiftUI マルチウィンドウサポートの完全実装ガイド
コメント
GitHub Issues · utterances