ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
visionOSでのHealthKitの導入
HealthKitを使用して空間キャンバスのメリットを存分に活かした体験を創出する方法を確認しましょう。Appleプラットフォーム上でHealthKitが提供する機能、既存のIPadOSアプリをvisionOSに展開する方法、ゲストユーザーのセッション中にHealthKitに関して特に注意すべき事項を解説します。また、SwiftUI、Swift Charts、Swiftの並行処理を利用してHealthKitで革新的な体験を実現する方法もご紹介します。
関連する章
- 0:00 - Introduction
- 1:52 - HealthKit in visionOS
- 3:48 - Spatial health experiences
- 7:47 - Guest User support
リソース
- Bringing multiple windows to your SwiftUI app
- Forum: Health & Fitness
- HealthKit
- Let others use your Apple Vision Pro
- Visualizing HealthKit State of Mind in visionOS
関連ビデオ
WWDC24
WWDC23
WWDC20
-
ダウンロード
こんにちは Zachです Sirindaです 私達はエンジニアとしてvisionOS ヘルスケア体験に取り組んでいます ご存じのとおり HealthKitはユーザーの ヘルスケアデータのリポジトリを 暗号化して集中管理します 許可を得ると このデータにアクセスして 健康の増進と 維持に役立つ体験を 生み出せるようになります
HealthKitには ヘルスケアデータのクエリと書き込み さらに時間の経過に伴うデータの集計 および統計計算の機能が含まれます またデータが更新された際に フォアグラウンドまたはバックグラウンドで 更新を受け取るためにアプリを 登録することもできます HealthKitには アクティビティ 心拍数 睡眠などの 広範なヘルスケアに関するデータや 予防接種や治療などの 臨床健康記録のデータも格納できます HealthKitの詳細については 2020年のセッション「Getting Started with HealthKit」をご覧ください 昨年 iPadOS 17のリリースに伴い HealthKitを 新しいプラットフォームに導入しました これにより このアプリRiseのように HealthKitに保存されたデータを利用して iPadで詳しく調べるという豊かな体験を 生み出せるようになりました その通りです そして visionOS 2では HealthKitがApple Vision Proで 利用できるようになりました 空間コンピューティングにより イマーシブかつ共同的な 幅広いヘルスケア体験を構築する 素晴らしい機会が得られます ここでは visionOS向けの HealthKit搭載アプリを いかに簡単に構築できるかをご紹介します このプレゼンテーションでは visionOSのHealthKitの機能を紹介します 次に 共有スペース内で 最適化された体験を生み出す方法を いくつか紹介します 最後に visionOSの ゲストユーザー機能と ゲストユーザー向けの HealthKitの機能について説明します まず 新しいプラットフォームで利用できる フレームワークの機能を 見てみましょう visionOSのHealthKitの動作は iPadOSと非常によく似ています アプリは データの読み書き 統計情報の計算 更新の登録を行うことができます 他に留意すべき点がいくつかあります 他のプラットフォームと同様に ユーザーは設定アプリで 各アプリのヘルスケアデータへの許可を 表示および編集できます また iCloudを通じて デバイス間で データを定期的に同期できます
visionOSのHealthKitは visionOS SDKを使用するアプリと iOS 17以降でコンパイルされた iPadOSアプリで利用できます 本日のセッションでは 新しいHealthKitの State of Mind APIを使用して カレンダーイベントに 対応する感情を記録できるアプリを visionOSに移行したいと考えています State of Mind APIの 使用方法について詳しくは 「Explore wellbeing APIs in HealthKit」をご覧ください
基本事項を確認しましょう HealthKitを導入する場合 アプリでヘルスケアデータが利用可能かどうか 確認することが重要です これはデバイスやOSのバージョンなどの 要因によって異なる場合があります visionOSの以前のバージョンを 引き続きサポートする場合は isHealthDataAvailable関数を使用して アプリの体験を 適切に調整してください ヘルスケアデータが 利用可能かどうかを確認したら アプリが処理しようとする データタイプの読み書きのために ユーザーに承認を要求する必要があります
healthDataAccessRequest関数は ヘルスケアデータへのアクセス許可をユーザーに 求めるプロンプトをトリガします アプリで必要な時に 必要なデータのみをリクエストしてください
これらのベストプラクティスに従うと iPadOSアプリをvisionOSで実行するための 更新が非常に容易になります 実際には 何もする必要がありません このアプリは 先ほどのAPIを使用して ヘルスケアデータの可用性をチェックするので iOS 17以降でコンパイルすると visionOS 2.0上でiPadでの体験が 得られるようになります アップデートは必要ありません これですぐに始めることができます ここでSirindaにバトンタッチして 空間コンピューティングに重点を置いた HealthKitアプリを 開発する際に考慮すべき ベストプラクティスを 紹介してもらいましょう
ありがとう Zach visionOS向けアプリのデザインが いかに簡単かをご紹介します まず アプリがvisionOSの デザインとインタラクションパターンを 採用していることを確認します これにより プラットフォームの強みを 活かせるようにします また 表示するヘルスケアデータに より多くの情報やニュアンスを加えるために 体験を洗練し強化できる可能性を探ります
その後 visionOSでHealthKitの体験を 本当に素晴らしいものにするために さらに上を目指す方法について インスピレーションを得るための 時間を取りたいと思います では始めましょう
まず アプリのターゲットとして visionOSを追加します これにより このプラットフォーム上で アプリを実行できるようになります を選択すると Zachが紹介した体験を得られます 今日はvisionOS SDKを最大限に活用するため を選択します
このアプリはiPadOSと同じ HealthKitデータ認証構成を使用します これでうまくいきます 既存のアプリをvisionOSに 移行する方法については 「Elevate your windowed app for spatial computing」をご覧ください 次に行きましょう visionOS向けのアプリを構築したので 標準のシステムの見た目が 自動的に採用されます TabViewやSheetsなどの 標準のSwiftUIコンポーネントは visionOSに適切に変換されます ヘルスケアデータは デバイス間で同期されるため Apple Vision Proでも同じグラフと インサイトを表示できます またインサイトはSwift Chartsを使用して 構成されているため すでに見栄えがよくなっています アプリがVision Proプラットフォームで 適切に表示されるようになったので この無限の空間キャンバスを どのように活用できるか見ていきましょう visionOSでの体験を向上させる変更を 既にいくつか行っています このタブでは グラフを動的に サイズ変更できるようにして ピンチ&ドラッグで アプリウィンドウを拡大すると さらに多くのデータを一目で 確認できるようになります
これを行うには 今年の新しいSwiftUI APIである onGeometryChangeを使用して グラフのサイズの変化を監視します ビューが大きくなると グラフの ポイントの数を増やすことができます
空間キャンバスには まだ余裕があるので 2つのグラフを並べて比較してみましょう マルチウインドウのサポートを 追加するのは簡単です 私のiPadアプリは既に Split Viewをサポートしているからです マルチウインドウのサポートを 設定する方法の詳細については セッションメモにリンクされている ドキュメントをご覧ください コードで 新しいグラフに 対応する識別子を持つ openWindowアクションを 呼び出すボタンを作成します このアクションにより 新しいウィンドウにグラフが開きます それだけではありません アプリ全体を書き直すことなく 完全なイマーシブ空間体験を 簡単に作成できます
例えば 感情をログに記録する 体験を刷新するために visionOSのイマーシブ機能を 活用したいと思います 例えば ブレインストーミングセッションを終えて 自分の気持ちを振り返ろうとしています
Apple Vision Proでは イマーシブ空間に入ることができます 他のアプリを非表示にして 周囲のパススルーを暗くすることで 仕事場から切り離された感覚が得られます 気を散らすものがない場所で 直近のカレンダーイベントを カラフルなイマーシブ体験を 通して振り返ります
をタップすると パススルーの色が消え アプリに戻って作業を再開できます これらすべてのアップデートにより アプリはvisionOSに馴染むようになり 既存の体験が強化され 新しい体験も生み出されます これでアプリが visionOS用にカスタマイズされました ここで もう1つすべきことがあります ゲストユーザーに関するものです
ゲストユーザーは 他のユーザーが Apple Vision Proをセットアップして 試すことができる visionOSの機能です これは空間コンピューティングを 体験するのに最適な方法であり 所有者のデータとプライバシーを 保護する機能も含まれています ゲストユーザーについて詳しくは Appleサポートの記事 「Let others use your Apple Vision Pro」をご覧ください
ゲストユーザーのセッション中 ゲストは所有者が承認した アプリを使用できます ほとんどの場合 体験は変わりませんが HealthKitを使用する場合は 特別な考慮事項がいくつかあります ゲスト用に実行しているアプリは すでに承認されていれば 引き続き HealthKitデータにアクセスできます ただし アプリはゲストに追加の HealthKit承認を要求することはできません 要求された場合 承認フローは表示されず 代わりにエラーを返します ゲストは visionOS設定で 認証 プライバシー または セキュリティオプションを 編集することもできません ゲストユーザーとして ヘルスケアデータを操作する際の もう1つの制限はデータの書き込みです ゲストユーザーセッション中は ヘルスケアデータの書き込みはできません これは ゲストによって生成されたデータが 所有者のヘルスケアデータと 混ざるのを防ぐためです セッション中にアプリが データを書き込もうとすると HealthKitは新しいエラーを返します このエラーを処理することで ゲストによって生成されたデータを 安全に破棄することができ 後で所有者の ヘルスストアに保存されないようにします これは データが保存されていないことを ゲストに通知するのに 適した場所でもあります ゲストユーザーについての説明 ありがとうございました これらのことを念頭に置いて アプリを更新しましょう Zachが説明したように 承認シートはゲストユーザに表示されません つまり ゲストユーザーセッション中の healthDataAccessRequestへの呼び出しは エラーが発生して失敗します リクエストは後回しにすることを お勧めします ゲストユーザーは データの書き込みを試みることもできます ただし サンプルはヘルスストアに 保存できません そこで表示されるエラーに対処できるように 既存のロジックを更新しました Zachが説明したように このエラーが表示された場合は ゲストが生成したヘルスケアデータのサンプルを 破棄することをお勧めします 最後に データが保存されていないことを ゲストユーザーに通知します これは ユーザーが感情を 選べるようにするコードです ここでは このボタンを修正し ゲストが感情を記録しようとして エラーが発生するたびに 警告を表示するようにしました ゲストユーザー体験がシームレスに なるようにアプリを設定したので 変更が意図したとおりに 機能することを確認しましょう ゲストユーザーとしてアプリを開くと ヘルスケアデータの承認を求める プロンプトは表示されません これは ゲスト用に実行されているアプリが 所有者から付与された承認を継承するためです そのため 所有者と同じように アプリを体験できます ここで感情を記録してみましょう このイベントをタップして選択します 完了すると ゲストユーザーとしての データが保存されていないことを 通知する警告が表示されます これで ゲストはデータを所有者の ヘルスストアに追加することなく アプリの雰囲気を感じることができます ゲストユーザーを処理できるように アプリを構成したので Apple Vision Proで友人や家族と アプリを共有する準備が ほぼ整いました 覚えておくべきベストプラクティスを いくつか紹介します ゲストユーザーはヘルスケアデータへの アクセス承認が制限されているため 承認呼び出しを必ずアップデートして このエラーを適切に処理してください
ゲストユーザーが書き込みを試みた ヘルスケアデータは必ず破棄してください これにより ゲストによって 生成されたデータが 所有者のヘルスケアデータと 混ざるのを防ぐことができます 最後に 所有者が既に アプリを承認している場合 アプリは引き続き ヘルスケアデータを 読み取れることに注意してください これらのベストプラクティスに従うことは ゲストユーザーと所有者の 両方でシームレスな体験を 維持するために不可欠です Sirinda ありがとう 素晴らしい説明でした 本日のまとめです HealthKitがvisionOSに導入され 新しいプラットフォームでヘルスケアデータの 読み取り 書き込み 観察が可能になりました 導入する際には 空間コンピューティングの体験を洗練させて 再考する機会を活用してください ゲストユーザーに対するHealthKitの 動作を考慮して コードを調整してください 詳細については これらのセッションをご覧ください また サンプルアプリをダウンロードして コードを確認してください visionOSに導入された HealthKitをぜひご活用ください 今後 皆さんが生み出す 新たな体験を楽しみにしています ご視聴ありがとうございました
-
-
2:43 - Check whether health data is available
import HealthKit if HKHealthStore.isHealthDataAvailable() { // Configure HealthKit-powered experiences } else { // Omit HealthKit experiences }
-
3:03 - 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
-
5:59 - 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 { @State 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 } } }
-
6:33 - Open chart as a new window
// Opens chart as a new window 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") } } }
-
9:00 - 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 } }
-
9:26 - Request authorization to State of Mind datatype
// Request authorization to State of Mind datatype @main struct HKStateOfMindDataSampleApp: App { @State var toggleHealthDataAuthorization = false @State 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 } ... } } } } }
-
9:42 - 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)") } } ...
-
9:58 - Present an alert with a message using the given details
// Present an alert with a message using the given details 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 }, // default OK button message: { details in Text(details.errorString) }) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。