まずは公式ドキュメントをちゃんと読む人間になろうと思いたち、Apple公式ドキュメントだけを元にHealthKitにアクセスを試みました。
環境
- XCode Version 13.0
- iOS target 14.0
参考(公式)
- 前説
- XCodeでHealthKitを有効化する
- プライバシーデータへアクセスするためのプロジェクト設定
- Swiftでヘルスケアデータにアクセス
プロジェクト設定
HealthKitの有効化(プロジェクト設定の「Signing & Capabilities」にHealthKitを追加
アクセス要求用のメッセージ定義
今回は体温データの入力のみなのでPrivacy - Health Update Usage Description
アプリが読み出しする場合はPrivacy - Health Share Usage Description
が必要になります
参考: Protecting User Privacy (developer.apple.com)
これらのDescriptionは13文字以上必要なようです。これに関しては調査力が足りず、StackOverflowのお世話になりました。
コード
今回は1つのクラス内でHealthKitへの参照を完結させます
setup()
HKHealthStore
はドキュメントによれば、無闇に生成せず保持し続けるのがいいようです。
You need only a single HealthKit store per app. These are long-lived objects; you create the store once, and keep a reference for later use.
エラーチェックで呼び出し側の処理を変えることなども考慮し、setup()メソッドでStoreの生成を行います。(エラーチェックが不要であればinit()内で生成してました)
postBodyTemperature()
以下の順番で処理を行います
- 体温データに関するアクセス許可取得
- アクセス許可状態の確認
- 体温データの保存
出来上がったClass
import Foundation
import HealthKit
enum BodyTemperatureUnit{
/// 摂氏
case degreeCelsius
/// 華氏
case degreeFahrenheit
}
class HealthCareRepository{
let allTypes = Set([HKObjectType.quantityType(forIdentifier: .bodyTemperature)!])
/// HKHealthStoreはアプリケーションあたり1インスタンス。1回生成したらそれを使い続ける必要あり
var store:HKHealthStore? = nil
func setup() -> Bool{
/// ipadではヘルスケア使えない
/// https://developer.apple.com/documentation/healthkit/setting_up_healthkit
/// Ensure HealthKit’s Availability
if (HKHealthStore.isHealthDataAvailable() == false){
// ヘルスデータが無効状態
return false
}
/// ヘルスケア機能があり、有効である場合生成する
self.store = HKHealthStore()
return true
}
func postBodyTemperature(_ value:Double, unit:BodyTemperatureUnit, completion:@escaping (Bool, Error?) -> Void) -> Void{
/// https://developer.apple.com/documentation/healthkit/authorizing_access_to_health_data
/// Request Permission from the User
/// toShare: Write要求
/// read: Read要求
self.store!.requestAuthorization(toShare: allTypes, read: nil){ (success, error) in
if !success{
completion(success, error)
return
}
/// https://developer.apple.com/documentation/healthkit/authorizing_access_to_health_data
/// Check for Authorization Before Saving Data
let status = self.store!.authorizationStatus(for: .quantityType(forIdentifier: .bodyTemperature)!)
switch status{
case .notDetermined:
// "If you have not yet requested permission"
// ここに入ることはないはず
print("Not determined")
completion(false, HKError(HKError.errorAuthorizationNotDetermined))
return
case .sharingDenied:// If the user has denied permission
// ユーザーが許可しなかった場合
print("Sharing Denied")
completion(false, HKError(HKError.errorAuthorizationDenied))
break
case .sharingAuthorized:
// ユーザーが許可した場合
print("Sharing Authorized")
break
@unknown default:
print("Unknown status.")
break
}
// Datetime
let now = Date()
// 摂氏 or 華氏
let hkUnit:HKUnit
switch unit {
case .degreeCelsius:
hkUnit = .degreeCelsius()
case .degreeFahrenheit:
hkUnit = .degreeFahrenheit()
}
let quantity = HKQuantity(unit: hkUnit, doubleValue: value)
let obj = HKQuantitySample(type: .quantityType(forIdentifier: .bodyTemperature)!, quantity: quantity, start: now, end: now)
self.store!.save(obj, withCompletion: completion)
}
}
}
結果
適当なUI作って上記クラスを試した結果、シミュレータ上ではありますが無事に体温データをヘルスケアに登録することができました。大抵のことは公式Documentに書いてあることも実感できました。次回はUI予定です。