Swift iOS HealthKit を使ってみる
HealthKitを使用することで、iPhoneが管理している、健康・フィットネスデータにアクセスし、ユーザーのプライバシーとコントロールを維持しながら、共有することができる。
HealthKitサイトを適当に機械翻訳しながら、要点をまとめて、少しずつ動かしてみる。
1.HealthKit概要
- HealthKitは、iPhoneとApple Watch上の健康・フィットネスデータの中央レポジトリを提供します。
- ユーザーの許可を得て、アプリはHealthKitストアと通信し、このデータにアクセスし共有します。
- HealthKitのアプリは、このエクスペリエンスを構築するために協調的なアプローチをとります。
- アプリは、これらの機能をすべて提供する必要はありません。その代わり、最も関心のあるタスクのサブセットにのみ焦点を当てることができます。
例えば、体重記録、歩数計算、健康チャレンジの各アプリを、ユーザーのニーズに合わせて選択することができます。
HealthKitのアプリは(ユーザーの許可を得て)自由にデータを交換できるため、このスイートは、単独のアプリよりもカスタマイズされたエクスペリエンスを提供します。
例えば、友人同士で毎日の歩数を記録するチャレンジに参加する場合、各自が好みのハードウェアデバイスとアプリを使って歩数を記録し、グループの全員が同じソーシャルアプリを使ってチャレンジすることができます。
また、HealthKitは、複数のソースからのデータを管理・マージするように設計されています。
例えば、ユーザーは、データの追加、データの削除、アプリの権限変更など、自分のすべてのデータをHealthアプリで表示、管理することができます。
そのため、アプリの外部でこれらの変更が発生した場合でも、アプリが対応する必要があります。
2.プライバシー
https://developer.apple.com/documentation/healthkit/protecting_user_privacy
- 健康データは機密性が高いため、HealthKitでは、アプリが共有できる情報をユーザーがきめ細かく制御できるようになっています。
- ユーザーは、各アプリにHealthKitストアへのデータの読み取りと書き込みの権限を明示的に付与する必要があります。
- ユーザーは、データの種類ごとに個別に許可を与えたり、拒否したりすることができます。
アプリは使用説明を含めなければ、HealthKitデータにアクセスするための認証を要求したときにクラッシュします。
Healthkit にデータを読み込むために NSHealthShareUsageDescription キー、およびデータを書き込むために NSHealthUpdateUsageDescription キーを含めます。
Xcode 13以降を使用して作成されたプロジェクトでは、アプリのInfoタブのTarget Propertiesリストでこれらのキーを設定します。
Xcode 12 またはそれ以前で作成されたプロジェクトでは、これらのキーをアプリの Info.plist ファイルで設定します。詳細については、「情報のプロパティリスト」を参照してください。
ユーザーのデバイスは、すべてのHealthKitデータをローカルに保存します。セキュリティのために、ユーザーがデバイスをロックすると、デバイスはHealthKitストアを暗号化します。
その結果、アプリがバックグラウンドで実行されている場合、アプリはストアからデータを読み取ることができない場合があります。
ただし、端末がロックされている場合でも、アプリはストアに書き込むことができます。
HealthKitはデータを一時的にキャッシュし、ユーザーが携帯電話のロックを解除すると同時に暗号化されたストアに保存します。
3.About the HealthKit Framework
- フレームワークは、データの種類と単位を定義済みのリストに制約し、すべてのアプリがデータの意味とその使用方法を理解できるようにします。
- 開発者は、カスタムのデータ型や単位を作成することはできません。その代わり、HealthKitは多種多様なデータ型と単位を提供します。
3.1 HealthKit Data
- HealthKitは、様々な種類のデータをHealthKit Storeに保存します。
3.2 特徴的なデータ
- これらのレコードは、ユーザーの生年月日、血液型、生物学的性別、肌タイプなど、通常変更されない項目を表します。
- このデータは、dateOfBirth(), bloodType(), biologicalSex(), fitzpatrickSkinType() メソッドを使用して、HealthKitストアから直接読み込むことができます。
- アプリケーションは特性データを保存できません。ユーザーは、Healthアプリを使用してこのデータを入力または変更する必要があります。
3.3 サンプルデータ
- ユーザーの健康データのほとんどは、特定の時点のデータを表すサンプルに保存されています。
- すべてのサンプル クラスは、HKSample クラスのサブクラスであり、HKObject クラスのサブクラスです。
- これらのクラスの詳細については、『Samples』を参照
3.4 ワークアウトデータ
- フィットネスやエクササイズのアクティビティに関する情報は、HKWorkoutサンプルとして保存されます。
- HKWorkoutはHKSampleのサブクラスですが、他のサンプルのサブクラスとは多少動作が異なります。
- これらの違いについては、「ワークアウト データ」で説明しています。
3.5 ソースデータ
- 各サンプルには、そのソースに関する情報が保存されています。
- HKSourceRevisionオブジェクトには、サンプルを保存したアプリまたはデバイスに関する情報が含まれています。
- HKDeviceオブジェクトには、データを生成したハードウェアデバイスに関する情報が含まれています。
3.6 削除されたオブジェクト
- HKDeletedObjectインスタンスは、HealthKitストアから削除されたアイテムのUUIDを一時的に保存するために使用されます。
- 削除されたオブジェクトを使用して、ユーザーまたは他のアプリによってオブジェクトが削除されたときに応答することができます。
- 詳細については、HKAnchoredObjectQueryおよびHKDeletedObjectを参照してください。
4.Setting Up HealthKit
HealthKitを使用する前に、以下の手順を実行する必要があります。
4.1 アプリでHealthKitを有効にします。
HealthKitを使用する前に、アプリのHealthKitケイパビリティを有効にする必要があります。
Xcode で、プロジェクトを選択し、HealthKit 機能を追加します。アプリがユーザーの臨床記録にアクセスする必要がある場合のみ、Clinical Health Records チェックボックスを選択します。
機能を有効にすることについての詳細な議論については、Xcode ヘルプの Configure HealthKit を参照
HealthkitエントリはWatchKitエクステンションでは使用されません。
4.2 現在のデバイスでHealthKitが利用可能であることを確認します。
isHealthDataAvailable()メソッドを呼び出し、ユーザーのデバイスでHealthKitが利用可能であることを確認
if HKHealthStore.isHealthDataAvailable() { // Add code to use HealthKit here. }
他のHealthKitのメソッドを呼び出す前に、このメソッドを呼び出します。HealthKitがデバイス上で利用できない場合(iPadなど)、他のHealthKitのメソッドはerrorHealthDataUnavailableエラーで失敗します。HealthKitが制限されている場合(例えば、エンタープライズ環境)、メソッドはerrorHealthDataRestrictedエラーで失敗します。
4.3 アプリのHealthKitストアを作成する。
HealthKitが有効かつ利用可能な場合、アプリのHKHealthStoreオブジェクトをインスタンス化します。
let healthStore = HKHealthStore()
HealthKitストアは、アプリごとに1つだけ必要です。これらは長寿命のオブジェクトです。ストアを一度作成し、後で使用するためにリファレンスを保持します。
4.4 データの読み取りと共有の許可を要求する。
5.Authorizing Access to Health Data
ユーザーのプライバシーを保護するために、HealthKitではきめ細かな認証が必要です。データへのアクセスや保存を試みる前に、アプリが使用する各データ型の読み取りと共有の両方について許可を要求する必要があります。ただし、すべてのデータ型に対して一度に権限を要求する必要はありません。むしろ、データへのアクセスが必要になってから許可を求める方が理にかなっている場以下の例では、消費エネルギー、サイクリング距離、ウォーキングまたはランニング距離、および心拍数サンプルの読み取りと共有の許可を求めるSpeedySlothアプリが表示されます。
let allTypes = Set([HKObjectType.workoutType(), HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, HKObjectType.quantityType(forIdentifier: .distanceCycling)!, HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!, HKObjectType.quantityType(forIdentifier: .heartRate)!]) healthStore.requestAuthorization(toShare: allTypes, read: allTypes) { (success, error) in if !success { // Handle the error here. } }
上記だけだと、以下のエラー
2022-05-31 22:57:12.407288+0900 HealthSample[1708:19257] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSHealthUpdateUsageDescription must be set in the app's Info.plist in order to request write authorization for the following types: HKQuantityTypeIdentifierHeartRate, HKQuantityTypeIdentifierDistanceWalkingRunning, HKWorkoutTypeIdentifier, HKQuantityTypeIdentifierActiveEnergyBurned, HKQuantityTypeIdentifierDistanceCycling'
info.plist に以下を記述する
<key>NSHealthShareUsageDescription</key> <string>読み込み許可を求めるメッセージ</string> <key>NSHealthUpdateUsageDescription</key> <string>書き込み許可を求めるメッセージ</string>
ここまでをシミュレーターで試してみる。
以下コード
import HealthKit import UIKit class ViewController: UIViewController { var isHealthAvailable : Bool! var healthStore : HKHealthStore! @IBOutlet weak var enabledHealthKitLabel: UILabel! @IBOutlet weak var healthStoreLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. checkHealthKit() } @IBAction func OnAccessRequest(_ sender: Any) { let allTypes = Set([HKObjectType.workoutType(), HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, HKObjectType.quantityType(forIdentifier: .distanceCycling)!, HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!, HKObjectType.quantityType(forIdentifier: .heartRate)!]) self.healthStore.requestAuthorization(toShare: allTypes, read: allTypes) { (success, error) in if !success { // Handle the error here. } } } func checkHealthKit() { self.isHealthAvailable = HKHealthStore.isHealthDataAvailable() self.enabledHealthKitLabel.text = "Health data available : \(self.isHealthAvailable!)" if self.isHealthAvailable { healthStore = HKHealthStore() } healthStoreLabel.text = "HealthStore : \(String(describing: self.healthStore))" } }