【Swift】HealthKitの使い方!ヘルスケア/フィットネスデータの取得
この記事からわかること
- Swift/iOSでHealthKitフレームワークの使い方
- ヘルスケア/フィットネスデータの取得
- HKHealthStoreクラスとは?
- 消費カロリーや歩数、ランニングデータ、睡眠、心拍などの取得方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:16.0
- iOS:18.0
- Swift:5.9
- macOS:Sonoma 14.6.1
HealthKitとは?
「HealthKit」とはiOS・watchOSにおいて健康データやフィットネスデータを管理、表示、共有機能など提供するフレームワークです。HealthKitフレームワークを使用することでデフォルトで入っている「ヘルスケア」や「フィットネス」アプリのデータを参照できるようになります。開発したアプリからデータを参照するためにはユーザーの明示的な許可が必要になります。
- 健康データ:身長や体重、心拍、睡眠などに関するデータ
- フィットネスデータ:歩数、距離、消費カロリー、ワークアウト(※)などに関するデータ
※ ワークアウトとはウォーキングやランニングなど特定の運動のパフォーマンスデータを追跡するための活動セッションのことです。Apple Watchには「ワークアウト」アプリがあり特定の運動を計測することが可能です。
HealthKitアプリを開発する前準備
開発アプリからHealthKit
を使用できるようにするためのプロジェクト側の設定を行っていきます。まず「General」>「Frameworks,Libraries,and Embedded Content」から「HealthKit」と「HealthKitUI」を追加します。
続いて「Signing & Capabilities」から「HealthKit」を追加します。アプリから「Clinical Health Records(臨床健康記録)」を参照したい場合のみ「Clinical Health Records」にチェックを入れておきます。チェック済みなのにアプリからデータを参照しない場合はリジェクトされる可能性もあるので注意してください。
info.plistに権限の追加
続いて「info.plist」にデータの読み取りや書き込み権限を追加し、説明を記述しておきます。適切に説明を追加しておかないとデータへのアクセス承認を実行時にアプリがクラッシュしてしまいます。
- NSHealthUpdateUsageDescription:データの保存
- NSHealthShareUsageDescription:データの読み取り
- NSHealthClinicalHealthRecordsShareUsageDescription:臨床データの読み取り(※)
- NSHealthRequiredReadAuthorizationTypeIdentifiers:読み取る臨床データタイプ(※)
※ 臨床データを取得したい場合のみ追加する必要があります。
これで準備が整いました。ここからは開発方法をまとめていきます。実装サンプルコードはGitHubで公開しているので参考にしてください。
HealthKitの主要なクラス
HealthKit
で実装するために主要となるクラスなどを先に軽くまとめておきます。
HealthKit Store
HealthKitのデータはiPhone・Apple Watch・visionOSごとに独立したHealthKit Storeに管理されています。独立はしていますがデバイス間でデータを自動的に同期しているので共通したデータを参照することができるようになっておりHKHealthStore
クラスとして定義されています。Apple Watchではスペースを節約するために定期的に古いデータは削除される仕様になっておりされる仕様になっておりearliestPermittedSampleDate()
メソッドを使用して最古のデータを参照することができるようになっています。
HKObject
HKObject
はHealthKitデータのスーパークラスです。後述するHKCharacteristic
やHKSample
などが継承しているクラスでデータベースのメタ情報などの保持を義務つげます。
HKCharacteristic
HKCharacteristic
は時間共に変化しないデータの型です。主に誕生日や血液型、生年月日などを取得できます。
HKSample
HKSample
は時間共に変化するデータの型です。開始時間と終了時間を明示的に指定して期間内のデータを取得します。HKSample
もスーパークラスとして定義されており後述するなどが継承しています。
HKQuantitySample
HKQuantitySample
はHKSample
を継承したクラスで値を数値で取得できるようなデータの型です。主に歩数や消費カロリー、移動距離などを取得できます。
HKCategorySample
HKCategorySample
もHKSample
を継承したクラスでカテゴリに分類できるようなデータの型です。主に睡眠分析や生理、食欲の変化などを取得できます。
HKSampleQuery
HKSampleQuery
はHKSample
を継承しているヘルスケアデータを取得するためのクエリオブジェクトです。取得したいデータ項目や期間、数量などを指定することができます。
データへの許可要求
HealthKitのデータはユーザーのプライバシーを保護するために認証なしに参照することができない仕様になっています。許可申請は読み取り/書き込みのデータごとに細かく申請できるようになっておりリクエストを実行する際に必要なデータタイプを指定する必要があります。この許可申請は一括で行うことも可能ですが、実際にデータが必要になるタイミングでの申請が公式からは推奨されています。
データごとの許可項目はHKCharacteristicType
やHKQuantityType
のイニシャライザなどから取得できます。許可申請したい項目をSet
でまとめておきrequestAuthorization(toShare:read:)
メソッドを使用して要求を出します。
/// 書き込み許可申請項目
public let writeAllTypes: Set = [
// 歩数
HKQuantityType(.stepCount),
// 消費エネルギー
HKQuantityType(.activeEnergyBurned),
// サイクリングの移動距離
HKQuantityType(.distanceCycling),
// ウォーキング・ランニングの移動距離
HKQuantityType(.distanceWalkingRunning),
// 車椅子ユーザーの移動距離
HKQuantityType(.distanceWheelchair),
// 心拍数
HKQuantityType(.heartRate),
// ワークアウト
HKQuantityType.workoutType()
]
/// 読み取り許可申請項目
public let readAllTypes: Set = [
// 性別
HKCharacteristicType(.biologicalSex),
// 血液型
HKCharacteristicType(.bloodType),
// 誕生日
HKCharacteristicType(.dateOfBirth),
// 歩数
HKQuantityType(.stepCount),
// 消費エネルギー
HKQuantityType(.activeEnergyBurned),
// サイクリングの移動距離
HKQuantityType(.distanceCycling),
// ウォーキング・ランニングの移動距離
HKQuantityType(.distanceWalkingRunning),
// 車椅子ユーザーの移動距離
HKQuantityType(.distanceWheelchair),
// 心拍数
HKQuantityType(.heartRate),
// ワークアウト
HKQuantityType.workoutType()
]
isHealthDataAvailable
で対象デバイスがそもそもHealthKitが有効なデバイスかどうかを識別できます。requestAuthorization(toShare:read:)
にはtoShare
に書き込み対象をSet<HKSampleType>
でread
には読み取り対象をSet<HKObjectType>
で渡します。ただプライバシー保護の一環で許可申請の承認/拒否はアプリから識別はできないようです。
/// HealthKit許可申請要求
private func requestAuthorization() async {
do {
// HealthKitが有効なデバイスかどうか
guard HKHealthStore.isHealthDataAvailable() else { return }
// 許可申請要求
// プライバシー保護の一環で許可申請の承認/拒否はアプリから識別はできない
try await healthStore.requestAuthorization(toShare: writeAllTypes, read: readAllTypes)
} catch {
// 失敗するのはアプリのInfo.plistの設定不足
// もしくは現在のデバイスで健康データが利用できない場合
fatalError("承認を要求中に予期しないエラーが発生しました: \(error.localizedDescription)")
}
}
このメソッドを実行すると以下のように指定した項目に許可チェックを行えるシートが起動します。
HealthKitデータの取得
HealthKitデータを取得する際にアクセス許可が承諾されていない場合は以下のようなエラーが発生します。
Error Domain=com.apple.healthkit Code=5 "Authorization is not determined" UserInfo={NSLocalizedDescription=Authorization is not determined}
HKCharacteristicデータ
HKCharacteristic
データを取得したい場合はHKHealthStore
から各項目のメソッドから取得することができます。例えば性別ならbiologicalSex
メソッドのbiologicalSex
プロパティからHKBiologicalSex
型で取得できます。
/// 特性情報を取得
public func readCharacteristic() {
// 性別を取得
guard let biologicalSex = try? healthStore.biologicalSex().biologicalSex else { return }
switch biologicalSex {
case .female:
print("性別: 女性")
case .male:
print("性別: 男性")
case .other:
print("性別: その他")
case .notSet:
print("性別: 未設定")
@unknown default:
print("性別: 不明")
}
// 年齢を計算
guard let dateOfBirth = try? healthStore.dateOfBirthComponents().date else { return }
let calendar = Calendar.current
let now = Date()
let ageComponents = calendar.dateComponents([.year], from: dateOfBirth, to: now)
guard let age = ageComponents.year else { return }
print("年齢: \(age) 歳")
// 血液型を取得
guard let bloodType = try? healthStore.bloodType().bloodType else { return }
switch bloodType {
case .aPositive:
print("血液型: A+")
case .aNegative:
print("血液型: A-")
case .bPositive:
print("血液型: B+")
case .bNegative:
print("血液型: B-")
case .abPositive:
print("血液型: AB+")
case .abNegative:
print("血液型: AB-")
case .oPositive:
print("血液型: O+")
case .oNegative:
print("血液型: O-")
case .notSet:
print("血液型: 未設定")
@unknown default:
print("血液型: 不明")
}
}
HKSampleデータ
HealthKitデータは項目ごとにHKSampleQuery
を定義してexecute
メソッドを使用して実行します。
public func reading() {
// 昨日の開始時刻と終了時刻を計算
let calendar = Calendar.current
// 昨日の開始時刻
let startOfYesterday = calendar.startOfDay(for: Date().addingTimeInterval(-86400))
// 昨日の終了時刻(開始時刻 + 1日)
guard let endOfYesterday = calendar.date(byAdding: .day, value: 1, to: startOfYesterday) else { return }
// 取得したいデータタイプ
let type = HKQuantityType(.activeEnergyBurned)
// 取得対象期間 Date型
let predicate = HKQuery.predicateForSamples(withStart: startOfYesterday, end: endOfYesterday, options: [])
// 取得数制限 HKObjectQueryNoLimit = 無制限
let limit = HKObjectQueryNoLimit
// 並び順
let sort = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true)]
// データ読み出しクエリ
let query = HKSampleQuery(
sampleType: type,
predicate: predicate,
limit: limit,
sortDescriptors: sort
) { [weak self] (query, results, error) in
guard let self else { return }
guard error == nil else { return }
guard let tmpResults = results as? [HKQuantitySample] else { return }
// 総カロリーを計算
let totalEnergyBurned = tmpResults.reduce(0.0) { sum, sample in
return sum + sample.quantity.doubleValue(for: .kilocalorie())
}
print("総消費カロリー: \(totalEnergyBurned) kcal")
self.kilocalorie = totalEnergyBurned
}
// クエリ実行
healthStore.execute(query)
}
HKSampleQuery
に引数にはそれぞれ以下の項目を渡す必要があり、結果は[HKSample]?
型で取得することができます。
- sampleType:取得したいデータタイプ
- predicate:取得対象期間 Date型
- limit:取得数制限
- sortDescriptors:並び順
プライバシー保護について
HealthKitデータに関してはプライバシーに関する制約がきめ細かく、ユーザーからのアクセスの承認も項目ごとにできると説明しました。それだけでなく公式ドキュメントにはプライバシーに関する情報はいくつか記述されています。
データの暗号化
HealthKitデータは端末にローカル保存されています。ユーザーがデバイスをロックしている際にはHealthKitデータは暗号化されるためバックグラウンド環境からデータを読み取れない可能性があるようです。ただロックされていても書き込み自体は問題なく行えるようです。
アプリ内での明示的な利用表示
HealthKit
の利用はApple公式から適切な場合かつ明示的な利用方法の開示をした上でないと利用できないと記述されています。
- 健康やフィットネスの目的での使用以外でHealthKit APIにアクセスしてはいけない
- 健康やフィットネスの目的の場合はマーケティングテキストとユーザーインターフェースの両方で利用方法を明記する
さらにHealthKitを使用したアプリには次のガイドラインが適用されるようです。
- HealthKitデータを広告や類似のサービスに使用することはできない
- HealthKitデータはユーザーの明示的な許可なく第三者に開示してはならない
- HealthKitデータは販売してはならない
- ユーザーが同意した場合、HealthKitデータを医療研究のために第三者と共有することができる
- あなたとあなたのアプリがユーザーのHealthKitデータをどのように使用するかをユーザーに明確に開示する必要がある
プライバシーポリシーの提供
HealthKitフレームワークを使用する全てのアプリはプライバシーポリシーを提供する必要があります。プライバシーポリシーの作成に関するガイダンスは以下サイトを参照。
個人健康記録モデル(HIPAA 非準拠アプリ用)
HIPAA モデル (HIPAA 対象アプリの場合)
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。