WWDC Quick Look 💓 By SwiftGGTeam
Get started with HealthKit in visionOS

Get started with HealthKit in visionOS

元の動画を見る

ハイライト

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 表示を行う。

関連セッション

コメント

GitHub Issues · utterances