初めてのHealthKit
HealthKitの概要
HealthKitはiPhoneとAppleWatchによって収集されたヘルスデータ(心拍数や睡眠 etc.)とフィットネスデータ(ランニングや水泳 etc.)の読み書きを行うためのAPIを提供しています。
ヘルスケアデータやフィットネスデータはAppleWatchに保存されますが、AppleWatchとペアリングしているiPhoneが近くにある場合は、そのiPhoneにデータが自動的にバックアップされます。
またiCloudへのバックアップを有効にしている場合は、iCloudにもデータがバックアップされます。
以下のようなイメージです。
HealthKitで扱うデータについて
HeakthKitで扱うデータは主にHKSample
というオブジェクトで表しています。
HKSample
は以下のような要素で構成されています。
- データタイプ(
HKSampleType
) - データの値(
HKSample
のサブクラスによってプロパティは異なる) - 記録した時間(
startDate
,endDate
) - データを作成したアプリやデバイスの情報(
device
) - メタデータ(
metadata
)
HealthKitで扱うデータタイプは多種多様であり、それらはHKSample
のサブクラスとして表されています。次にその一例としてHKQuantitySample
について見ていきます。
データを保存する
HealthKitで扱うデータについて知るために、試しに歩行距離を保存してみます。流れとしては以下のとおりです。
- 書き込み権限のリクエスト
- サンプルデータの作成
- HealthKitにデータを保存する
書き込み権限のリクエスト
// 1. アクセスしたいデータタイプを指定します。
let distanceType = HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)
// 2. toShareには書き込みしたいデータタイプを指定しています。
healthStore.requestAuthorization(toShare: [distanceType], read: nil) { success, error in
if success {
} else {
}
}
-
HKObjectType
に特定のデータタイプを表すオブジェクトを返すクラスメソッドが用意されています。 歩行距離は量を表すデータであるため、量を表すデータを書き込めるようにquantityType(forIdentifier:)
を使用してHKQuantityType
を取得します。。forIdentifier
にはどのような量のデータがほしいかを指定します。今回は歩行距離のデータを書き込みたいためdistanceWalkingRunning
を指定しています。 -
healthStore.requestAuthorization
でヘルスデータへのアクセス権限をリクエストします。今回は歩行距離のデータのみを書き込みたいのでtoShare
にdistanceType
を指定します。
歩行/走行距離のデータの作成と保存
次に歩行/走行の距離を保存してみます。
// 1. データのタイプを指定
let distanceType = HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!
// 2. データを記録した日時
let startDate = Calendar.current.date(bySettingHour: 14, minute: 35, second: 0, of: Date())!
let endDate = Calendar.current.date(bySettingHour: 15, minute: 0, second: 0, of: Date())!
// 3. 値
// HKQuantityは、値と単位を持っています。
let distanceQuantity = HKQuantity(unit: .meter(), doubleValue: 628.0)
// 4. 上記の要素を一つのデータとしてまとめる
let sample = HKQuantitySample(
type: distanceType,
quantity: distanceQuantity,
start: startDate,
end: endDate
)
// 5. データを保存
healthStore.save(sample) { success, error in
if success {
} else {
}
}
- 保存したいデータのタイプを指定します。
distanceWalkingRunning
は歩行距離と走行距離のHKQuantityType
です。 - データを記録した日時を指定します。
-
HKQuantity
は量に関するデータを表すオブジェクトです。 -
HKQuantitySample
でこれまで用意したコンポーネントをまとめます。 -
HKHealthStore
でクエリを実行し、データを保存します。クロージャには保存の成否がコールバックされます。
これでサンプルデータの保存が完了しました。
今回は距離に関するデータを扱いたかったためHKQuantitySample
を使用しましたが、
当然HKQuantitySample
が向かないデータもあります。睡眠状態やワークアウトなどのデータです。
睡眠状態のデータは量で表すことができません。起きている、ベッドにいる、睡眠中、など定量的なデータではなく定質的なデータで表されます。
試しに睡眠状態も保存してみます。
睡眠状態のデータの作成と保存
定量的なデータタイプはHKQuantityType
で表していましたが、定質的なデータはHKCategoryTtpe
で表します。
また睡眠状態のデータは表すにはsleepAnalysis
を用います。これらを用いると以下のような感じのデータを作成します。
時間 | 状態 |
---|---|
00:00 ~ 01:00 | ベッドいる |
01:00 ~ 07:00 | 睡眠中 |
07:00 ~ 07:05 | 起床 |
以下は01:00~07:00まで睡眠中状態であることを保存する処理です。
// 1. データのタイプを指定
let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
// 2. 開始/終了時刻
let startDate = Calendar.current.date(bySettingHour: 01:00, minute: 00, second: 0, of: Date())!
let endDate = Calendar.current.date(bySettingHour: 07:00, minute: 00, second: 0, of: Date())!
// 3. 上記の要素を一つのデータとしてまとめる
let sample = HKCategorySample(
type: sleepType,
value: HKCategoryValueSleepAnalysis.inBed.rawValue,
start: startDate,
end: endDate
)
// 4. 上記の要素を一つのデータとしてまとめる
self.healthStore.save(sample) { success, error in
if success {
print("success")
} else {
print("failed")
}
}
基本的な流れは、歩行/走行距離を保存した時と同じですが、データを一つにまとめる際のクラスが異なります。睡眠状態は定質的なデータであるため、HKCategorySample
を使用し、value
引数はInt
を取り、ベッドにいる、睡眠中、起床のそれぞれの状態に対応しています。
このように扱うデータのタイプに応じて、Healthkitで用意されているものの中から適当なオブジェクトを選択するようにしてください。
ここまでのまとめ
ここまでHealthKitでデータを扱うためにまずはデータ構造について見てきました。
HealthKitで扱うデータで重要なことはデータのタイプです。具体的には、
- 定量的なデータのタイプは
HKQuantityType
、定質的なデータのタイプはHKCategoryType
- それに対応するデータは
HKQuantitySample
、HKCategorySample
のように表され、クラスの継承関係は以下のようになっています。
HKObjectType
/HKObject
がルートクラスであり、データを作成したアプリやデバイスの情報やメタデータなどを持っています。
そしてそのサブクラスにHKSampleType
/HKSample
があり、これはデータを記録した日時などを持っています。
そして最後に具体的なデータを表すHKQuantityType
/HKQuantitySample
、HKCategoryType
/HKCategorySample
などがあり、それぞれに適したデータを持っている、というような階層になっています。
データの取得
データの作成/保存方法について見たので、次にデータの取得方法についても見ていきます。
HealthKitでヘルスデータを取得するには、クエリを使用します。
様々なクエリが用意されており、用途によって使い分けます。
ここでは、HKStatisticsQuery
とHKStatisticsCollectionQuery
、HKSampleQuery
について見ていきます。
まずはHKStatisticsQuery
について見ていきます。
1週間の歩行/走行距離の取得
名前の通りHKQuantitySample
の統計値を計算するクエリです。(統計値を計算するとは、平均値や最大値、最小値などを求めるということです。)
ではどのような統計値を計算できるか確認するために、例として1週間の歩数を取得してみます。
let distanceType = HKObjectType.quantityType(forIdentifier: .stepCount)!
let startDate = DateComponents(year: 2021, month: 6, day: 15)
let endDate = DateComponents(year: 2021, month: 6, day: 22)
let predicate = HKQuery.predicateForSamples(
withStart: Calendar.current.date(from: startDate)!,
end: Calendar.current.date(from: endDate)!
)
let query = HKStatisticsQuery(
quantityType: distanceType,
quantitySamplePredicate: predicate,
options: [.cumulativeSum]) { query, statistics, error in
print(statistics!.sumQuantity()!) // 6902 count
}
healthStore.execute(query)
日毎の歩数を取得
先程は1週間の歩数の合計を取得しましたが、
日毎の歩数パターンを知りたい場合、日毎の歩数を取得しなければなりません。
まずは1週間の毎日の歩数を取得してみます。前述のコードの日付を変えるだけです。
let startDate = DateComponents(year: 2021, month: 6, day: 15)
let endDate = DateComponents(year: 2021, month: 6, day: 16)
ただ1週間なら良いですが、1年間の毎日の歩数を取得するとなると300個以上のHKStatisticsQuery
を生成することとなり、対応しきれません。
解決方法はHKStatisticsCollectionQuery
使用することです。
これは指定した期間の統計値を問い合わせるクエリです。
var calendar = Calendar.current
let stepCountType = HKObjectType.quantityType(forIdentifier: .stepCount)!
let startDate = DateComponents(year: 2021, month: 4, day: 23, hour: 0, minute: 0, second: 0)
let endDate = DateComponents(year: 2021, month: 4, day: 29, hour: 23, minute: 59, second: 59)
let predicate = HKQuery.predicateForSamples(
withStart: calendar.date(from: startDate),
end: calendar.date(from: endDate)
)
let query = HKStatisticsCollectionQuery(
quantityType: stepCountType,
quantitySamplePredicate: predicate,
options: .cumulativeSum,
anchorDate: Calendar.current.date(from: anchorDate)!,
intervalComponents: DateComponents(day: 1)
)
// クエリの実行結果のコールバックハンドラーです
query.initialResultsHandler = { query, collection, error in
collection?.enumerateStatistics(
from: calendar.date(from: startDate)!,
to: calendar.date(from: endDate)!
) { statistics, stop in
print(statistics.sumQuantity() ?? "nil")
}
}
healthStore.execute(query)
こうすることで2021/4/23〜2021/4/29までの各日の歩数を取得できます。
またHKStatisticsCollectionQuery
ではstatisticsUpdateHandler
を用いることで歩数の変更を監視することができます。
Discussion
HealthKitを使用して歩数計アプリを作成しようとしているものです。
記事とても参考になりました。ありがとうございます。
1つお聞きしたいことがあります。
HealthKitがどの程度、過去のデータを取得できるのか?がわからないのでデータベースに保存するという方法が良いのかなと思っているのですが、もしご存知であれば過去のデータをどの程度取得できるのかをご教授頂けると助かります。
よろしくお願いいたします。
どこまでのデータが取得可能か確認する一番手っ取り早い方法は、純正のヘルスケアアプリで参照できるデータを確認することです。ヘルスケアアプリで参照できるデータは全てHealthKitからでも取得可能です。
コメントに返信ありがとうございます。
ヘルスケアアプリで参照できるデータに関しては取得可能ということですね。
分かりやすい回答ありがとうございます。