asken テックブログ

askenエンジニアが日々どんなことに取り組み、どんな「学び」を得ているか、よもやま話も織り交ぜつつ綴っていきます。 皆さまにも一緒に学びを楽しんでいただけたら幸いです!

WWDC22 What's new in HealthKit のまとめ

あすけんiOS app担当の @sato-shin です。 あすけんは食事記録に特化したヘルスケアサービスを開発しています。 ヘルスケアアプリを作る時には、企画側から HealthKit連携を求められることが多いです。

HealthKitはiPhoneなどにプリインストールされるハートマークアイコンのあのアプリの情報をRead/Writeするフレームワークです。

あすけんでもHealthKitを利用しており、日々情報をキャッチアップしています。 今回の記事では WWDC22 What's new in HealthKit の更新をまとめて紹介します。

※ まだβ版なので、変更される可能性があることにご注意ください。

1. Sleep analysis

AppleWatchの睡眠トラッキングの進化により睡眠段階の取得ができるようになりました。 この進化によりHealthKitの睡眠情報にも変更がありました。 HKCategoryValueSleepAnalysis に値の変更と追加があります。

以下の表が変更点のまとめです。

iOS 15 iOS 16 説明
in bed 変更なし ベッドに横たわっている状態
asleep asleep unspecified 破壊的変更。睡眠中(睡眠段階が未定義)
awake 変更なし 起床
- asleep core ノンレム睡眠ステージ 1, 2
- asleep deep ノンレム睡眠ステージ 3, 4
- asleep REM レム睡眠

睡眠の深さについて

AASM(American Academy of Sleep Medicine) で定義される睡眠の深さはノンレム睡眠(4ステージ)とレム睡眠に分かれています。 ノンレム睡眠のステージ1と2はasleepCore、3と4はasleepDeepになります。 数字が大きいほど深い眠りを意味し、レム睡眠は覚醒の準備状態のようです。

日本でも多くの場合で同じ指標が使われています。

睡眠取得についての変更点

これまではin bedを除外した睡眠状態のみを取得するにはasleepでフィルターすればよかったのですが、 睡眠段階が導入されたことでフィルターの方法が変わるので要注意です。 フィルターを asleep -> asleepUnspecified に単純に変更してしまうと、睡眠段階対応端末では睡眠が取得できなくなります。 そこで allAsleepValues というものが追加されました。

具体的には以下のように変更が必要です。

  • ~ iOS 15

      let predicate = HKCategoryValueSleepAnalysis.predicateForSamples(.equalTo, value: .asleep)
    
  • iOS 16 ~

      let predicate = HKCategoryValueSleepAnalysis.predicateForSamples(.equalTo, value: .allAsleepValues)
    

取得の際にHKSampleQueryを使う方法では睡眠段階毎のデータが返ってくるので、取得後の処理はアプリによっては変更が必要かもしれません。 統計情報を扱うHKStatisticsCollectionQueryを使う方法では、取得後の処理に変更はなくても大丈夫だと思われます。

2. Swift async

iOS 15.4からHKAsyncQuery protocolが導入されました。 これまではHKSampleQueryや、HKStatisticsCollectionQueryにcallback関数を設定していましたが、 HKAsyncQueryに準拠するHKSampleQueryDescriptorHKStatisticsCollectionQueryDescriptorを使うことで、取得にasyncを利用できるようになります。

Sample query

例として、従来のHKSampleQueryと、async対応のHKSampleQueryDescriptorを使ったコードサンプルを以下に示します。

  • HKSampleQuery(iOS 8.0+)

      let store = HKHealthStore()
      let query = HKSampleQuery(sampleType: HKCategoryType(.sleepAnalysis),
                                predicate: nil,
                                limit: HKObjectQueryNoLimit,
                                sortDescriptors: nil) { (query, samples, error) in
          /* 処理 */
      }
      store.execute(query)
    
  • HKSampleQueryDescriptor(iOS 15.4+)

      let store = HKHealthStore()
      let predicate = HKSamplePredicate.sample(type: HKCategoryType(.sleepAnalysis))
      let descriptor = HKSampleQueryDescriptor(predicates: [predicate], sortDescriptors: [])
      let samples = try async descriptor.result(for: store)
      /* 処理 */
    

Statistics collection query

統計情報を取得するHKStatisticsCollectionQueryではstatisticsUpdateHandlerで情報の更新をハンドリングすることが可能でした。

こちらも、例として従来のHKStatisticsCollectionQueryと、async対応のHKStatisticsCollectionQueryDescriptorを使ったコードサンプルを以下に示します。

  • HKStatisticsCollectionQuery(iOS 8.0+)

      let store = HKHealthStore()
      let query = HKStatisticsCollectionQuery(quantityType: HKCategoryType(.sleepAnalysis),
                                              quantitySamplePredicate: nil,
                                              options: .cumulativeSum,
                                              anchorDate: anchorDate,
                                              intervalComponents: intervalComponents)
      query.initialResultsHandler = { query, statisticsCollection, error in
          /* 処理 */
      }
      query.statisticsUpdateHandler = { query, statistics, statisticsCollection, error in
          /* 処理 */
      }
      store.execute(query)
    
  • HKStatisticsCollectionQueryDescriptor(iOS 15.4+)

      let store = HKHealthStore()
      let predicate = HKSamplePredicate.sample(type: HKCategoryType(.sleepAnalysis))
      let descriptor = HKStatisticsCollectionQueryDescriptor(predicate: predicate, 
                                                             option: .cumulativeSum, 
                                                             anchorDate: anchorDate, 
                                                             intervalComponents: intervalComponents)
      for try await result in descriptor.results(for: store) {
          /* 処理 */
      }
    

descriptor.results(for: store) の戻り値はAsyncSequenceに準拠しています。 for await ... in が使え、スッキリ書けるようになりましたね👀

宣伝: HKStatisticsQueryってなんだ?って人は、私が書いた記事を読んでいただければと思います🙇‍♀️ * iOSで正しく歩数を取得する

3. Workouts

iOS16, WatchOS9から運動(ワークアウト)が大きく変わります。

Workout Activity

~ iOS 15

HKWorkoutを使ってランニングや水泳、サイクリングなどを組み合わせた一連の運動情報を取得していました。 iOS15までのHKWorkoutでは1つの運動しか表すことができず、インターバルトレーニング(同一の運動を休憩を挟みながら行う)や、トライアスロンなどの複数の運動を組み合わせるというのができませんでした。 これでは各運動と全体を統合して分析するのはかなり面倒でした。


iOS 16 ~

この問題を解決するために、iOS16, WatchOS9からHKWorkoutActivity というクラスが追加されました。 1つのHKWorkoutは複数のHKWorkoutActivityで構成されるようになります。また、HKWorkoutActivityには各Activityの移行(休憩)期間も設定できるようになったことで、各運動の間の分析もできるようになりました。

さらにHKWorkoutとHKWorkoutActivityの両方に statisticsメソッドがあることに注目です。 たとえば、Workout全体の最高心拍数を取得するにはHKWorkout Objectのstatisticsメソッドを利用すれば良いし、各WorkoutActivityの最高心拍数を取得するにはHKWorkoutActivity Objectのstatisticsメソッドを利用すればよくなります。

また、HKWorkoutから取得できた総移動距離総消費カロリー水泳の総ストローク回数 が非推奨となります。 これらの情報を取得するにもstatisticsメソッドを使えばよくなり、インターフェイスがシンプルになります。

このようにインターフェイスが変化することで、 運動分析の柔軟性が飛躍的に向上します。

Workout Session & Builder

HKWorkoutActivityが追加されたため、 Workout Sessionには beginNewActivityメソッドendCurrentActivityメソッド が追加されます。 Workout Builderには addWorkoutActivityupdateActivityメソッド が追加されます。

Workout Session & Builderはざっくり以下のようなものです。

  • Workout SessionはWatchOSで利用できる、運動しながらリアルタイムにWorkoutを記録するクラス
  • Workout BuilderはiOS, WatchOSの両方で使える、Workoutを組み立て記録するクラス

New Metrics

AppleWatch Series6, SE以上では以下のメトリクスが取れるようになります。

Running

  • Stride length(ランニングの歩幅の長さ)
  • Power(ランニングパワー)

Swimming

  • SWOLF Score(水泳効率)

Heart rate recovery

iOS 16から「運動後に心拍数がどれくらい早く下がるか」が推定できるようになります。 ヘルスケアアプリ内ではCardioRecovery(心拍数回復)という項目で見ることができます。

HealthKitのQuantityTypeは heartRateRecoveryOneMinutes という名前になっています。

これで取得できる値は、「運動終了の1分後にどのくらい心拍数が下がるか」の推定値になっています。 また、このデータのメタデータとして以下の情報を追加できます。

  • HKMetadataKeyHeartRateRecoveryTestType
    • HKHeartRateRecoveryTestTypeの中から選択する
      • 力を振り絞った(maxExercise)
      • トレーニングではない行動だった(predictionNonExercise)
      • 最大下運動だった(predictionSubMaxExercise)
  • HKMetadataKeyHeartRateRecoveryActivityType
    • 運動の種類(ランニング、水泳など)
  • HKMetadataKeyHeartRateRecoveryDuration
    • 運動を行なった時間
  • HKMetadataKeyHeartRateRecoveryMaxObservedRecoveryHeartRate
    • 運動中の最高心拍数

4. Vision prescriptions

世界的にみて眼鏡やコンタクトレンズの購入には眼科からの処方箋が必要です。 その処方箋データを管理することができるようになりました。 アプリから利用するにはかなり特殊な情報ですが、眼鏡やコンタクトレンズは多くの人が日常的に使うものです。 眼鏡・コンタクトレンズ事業をやっている会社だと、かなり革命的な機能追加ですね。

ちなみに眼鏡の購入に処方箋が必要ない日本は世界的にみて珍しいみたいです。

扱える情報

視力についてはあすけんの対象領域ではないので、誤った情報を避けるため詳細なことには触れず、 ドキュメントの場所とヘルスケアアプリから扱える情報を貼っておきます。

https://developer.apple.com/documentation/healthkit/hkvisionprescriptiontypeidentifier

  • レンズの種類(眼鏡orコンタクトレンズ)
  • コンタクトレンズのブランド
    • コンタクトレンズのみ
  • 処方箋の発行日
  • 処方箋の有効期限
  • 処方箋の画像
  • 右・左の球面度数
  • 右・左の乱視度数
  • 右・左の円柱軸
  • 右・左の加入度数
  • 右・左の瞳孔間距離(PD)
    • 眼鏡のみ
  • 右・左の頂点間距離
    • 眼鏡のみ
  • 右・左のプリズム
    • 眼鏡のみ
  • 右・左のベースカーブ
    • コンタクトレンズのみ
  • 右・左のレンズの直径
    • コンタクトレンズのみ

5. macOS

これはWhat's new in HealthKitで触れられていませんでしたが、macOS 13から、HealthKitが利用可能になります。 もちろんMacBookにモーションセンサーはついていないため、バックに入れて持ち歩いたとしても、歩数のカウントなどはできないとは思いますが、iPhoneやAppleWatchなどで取得した情報を管理するためのアプリが作れるようになります。

選手がAppleWatchを付けていれば、コーチはmacで選手の状態をリアルタイムに確認したり、練習の強度をチェックするというサービスなんかは簡単に作れそうですね。

ヘルスケアに興味のあるエンジニアを探してます

この記事を読んで「ヘルスケア面白そう」と思った方の応募をお待ちしております!

www.wantedly.com

www.wantedly.com