【HealthKit】心拍数データを取得:最高・最低・平均・最新の値を確認する方法
HealthKitを用いて心拍数データを取得する
この記事では、HealthKitを使用して、iOSデバイスから心拍数データを取得し、最高・最低・平均・最新の心拍数を取得する方法を紹介します。
HKStatisticsCollectionQueryDescriptorを使用
HKStatisticsCollectionQueryDescriptor
とは、HealthKitで用いるデータ取得のためのクエリの一種です。データ取得をしたい開始時点と終了時点を一定のインターバルで区切りデータを取得し、インターバル毎の合計値・平均値・最大値・最小値を算出します。例えば過去一週間を1日毎に区切り、歩数の合計値を取得したい場合に使用します。
実装
今回は心拍数データを使用します。過去一週間を1日毎に区切り、1日毎の心拍数の平均値・最大値・最小値・最新(1日毎の最後のサンプルデータ)の値を取得します。
それでは、実際のコードを交えて説明いたします。
カレンダーと日付の設定
let calendar = Calendar(identifier: .gregorian)
let endDate = calendar.startOfDay(for: Date())
guard let startDate = calendar.date(byAdding: .day, value: -6, to: endDate) else {
fatalError("開始日時の計算に失敗しました")
}
ここでは、グレゴリオ暦のカレンダーを作成し、データ取得終了時点を実行した日のAM0:00をendDate
とします。その後、開始日を計算するためにendDate
から6日前の日付を計算します。
データ取得のための条件を作成
let thisWeek = HKQuery.predicateForSamples(withStart: startDate, end: endDate)
let heartRateType = HKQuantityType(.heartRate)
let heartRatesThisWeek = HKSamplePredicate.quantitySample(type: heartRateType, predicate: thisWeek)
- 指定した期間の心拍数データを取得するための条件(
NSPredicate
)の定義します。 - 取得したいデータタイプである心拍数(
HKQuantityType
)を指定します。
上記の2点の条件を用いて、HKSamplePredicate<HKQuantitySample>
であるheartRatesThisWeek
を宣言します。
HKStatisticsCollectionQueryDescriptorの設定
let heartRateQuery = HKStatisticsCollectionQueryDescriptor(
predicate: heartRatesThisWeek,
options: [.mostRecent ,.discreteAverage ,.discreteMax ,.discreteMin ,.separateBySource],
anchorDate: endDate,
intervalComponents: everyDay
)
心拍数のデータを取得するためのクエリを設定します。必要な計算処理に関するoption
を指定しています。option
では、以下のHKStatisticsOptions
を指定できます。
変数名 | 説明 |
---|---|
separateBySource |
指定された統計情報を各ソース(端末情報・アプリ)ごとに個別に計算することを示すオプション。 |
discreteAverage |
サンプルの平均値を計算することを示すオプション。 |
discreteMin |
サンプルの最小値を計算することを示すオプション。 |
discreteMax |
サンプルの最大値を計算することを示すオプション。 |
cumulativeSum |
サンプルのすべての値の合計を計算することを示すオプション。 |
mostRecent |
一致するサンプルから最新の値を返すことを示すオプション。 |
取得したい値の単位
let countPerMinuteUnit = HKUnit.count().unitDivided(by: .minute())
値に適した単位を紐づける必要があります。心拍数とは1分間のうちに心臓が拍動する回数であるため、回数を分で割る単位を定義します。
心拍数データの取得と処理
try await heartRateQuery.result(for: healthStore)
.statistics().forEach { statistics in
let average = statistics.averageQuantity()!.doubleValue(for: countPerMinuteUnit)
let min = statistics.minimumQuantity()!.doubleValue(for: countPerMinuteUnit)
let max = statistics.maximumQuantity()!.doubleValue(for: countPerMinuteUnit)
let mostRecent = statistics.mostRecentQuantity()!.doubleValue(for: countPerMinuteUnit)
// let sum = statistics.sumQuantity()!.doubleValue(for: countPerMinuteUnit)
let stats = DailyHeartRateStats(
date: dateFormatter.string(from: statistics.startDate), // 日付を文字列に変換
average: average, // 平均心拍数
min: min, // 最低心拍数
max: max, // 最高心拍数
mostRecent: mostRecent // 最新の心拍数
)
dailyHeartRates.append(stats)
}
作成したクエリを実行します。心拍数データを非同期で取得し、結果はHKStatistics
として受け取ることが可能です。HKStatistics
で心拍数の値(平均、最小、最大、最新)を取り出して、最終的にDailyHeartRateStats
オブジェクト(筆者が定義した構造体)に格納しています。
ここで注意するポイントとしては、
- クエリ作成の時、optionに必要な計算処理を設定していなければ値を取得できません
- 今回は心拍数データを使用するため、心拍数を合計しても意味を持たない値が算出されるため、
statistics.sumQuantity()
を呼び出しても実行時エラーが出力されます。そのため今回はコメントアウトで実装しています
2点目は下記のようなエラーが出力されます。
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Statistics option HKStatisticsOptionCumulativeSum is not compatible with discrete data type HKQuantityTypeIdentifierHeartRate'
捕捉されなかった例外 'NSInvalidArgumentException'、理由: '統計オプション HKStatisticsOptionCumulativeSum は離散データ型 HKQuantityTypeIdentifierHeartRate と互換性がありません' のためアプリを終了します。(DeepL翻訳)
以上で、クエリを作成して実行するまでの実装できました。
最後に
間違い・気になる部分がありましたら、コメントいただけると大変うれしいです。
良かったと思ったら記事へのいいね、Xのフォローをよろしくお願いいたします。
個人でアプリを作成しています。良かったら覗いてみてください。
Discussion