Sponsor Link
環境&対象
- macOS Monterery beta 5
- Xcode 13 beta5
- iOS 15 beta
HealthKit のセットアップ
HealthKit の async/await 対応したセットアップは、以前説明しました。
[iOS][HealthKit] HealthKit のセットアップ async/await 対応板
HealthKit へのアクセス
セットアップができている前提で、HealthKit へのアクセス方法を説明していきます。
HealthKit への書き込み
HealthKit への書き込みは、save メソッドで行います。save メソッドは、async/await 対応されているメソッドです。
func save(_ object: HKObject) async throws
Apple のドキュメントは、こちら。
ですので、作成した HKQuantitySample 等の保存する関数を async で作ると 以下のようになります。
func save(_ type: HKQuantityType, _ sample:HKQuantity) async -> Bool {
// (1)
guard healthStore.authorizationStatus(for: type) == .sharingAuthorized else { return false }
do {
// (2)
try await healthStore.save(sample)
} catch {
logger.error("\(error.localizedDescription)")
return false
}
return true
}
- アクセス前に、authorizationStatus を確認します。必要な許諾がなければ終了です
- async 対応した save を呼び出します
この save メソッドも、async 指定されていますので、呼び出し側は、await を付与して呼び出す必要があります。
async メソッドの終了を待たない 呼び出し方
HealthKit への書き込みを待たずに別の処理に進みたい時には、以下のように呼び出します。
Task {
_ = await save(type,sample)
}
processSomethingNext()
Task の中では、await 指定により save を待ちますが、processSomethingNext は、save 終了を待たずに実行されます。
async メソッドの終了を待つ呼び出し方
HealthKit への書き込みを待って 別処理を行いたいときは、以下のように呼び出す必要があります。
Task {
_ = await save(type,sample)
processSomethingNext()
}
await 指定で呼び出していますので、save 処理終了後に、processSomethingNext が呼び出されます。
HealthKit からの読み込み
HealthKit からデータを読み込むには、Query を使用する必要があります。
Query を使って、HealthKit からデータを取得する方法に async 対応されたものは用意されていません。
従来通りの Query を使ったデータ取得
func getHKDataFromHeatlKit() {
let typeDescriptor = HKQueryDescriptor(sampleType: sometype, predicate: nil)
let query = HKSampleQuery(queryDescriptors: [typeDescriptor], limit: Int(HKObjectQueryNoLimit)) { (query, samples, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
if let samples = samples {
// samples を保存
}
return
}
return
}
self.healthStore.execute(query)
いわゆる completionHandler を使った処理です。
Query に completion Handler をセットしておいて、HealthKit 経由で Query 実行したときに、completionHandler が呼ばれる形です。
async 対応した データ取得関数を作る
では 自分で async 対応したデータ取得関数を作ろうと考えると、取得してきたデータをどのように 関数返り値に反映させるかという点で困ってしまいます。
このような時に使用する概念が Continuation です。以下のように使用することで、completionHandler を使用した処理を async 対応に変換することができます。
以下では、エラー発生時には、空配列を返しています。エラー伝達が必要であれば、例外を使用します。
func hkData() async -> [HKQuantitySample] {
return await withCheckedContinuation { continuation in
let typeDescriptor = HKQueryDescriptor(sampleType: sometype, predicate: nil)
let query = HKSampleQuery(queryDescriptors: [typeDescriptor], limit: Int(HKObjectQueryNoLimit)) { (query, samples, error) in
if let error = error {
print("\(error.localizedDescription)")
continuation.resume(returning: [])
} else if let samples = samples as? [HKQuantitySample] {
continuation.resume(returning: samples)
} else {
continuation.resume(returning: [])
}
}
self.healthStore.execute(query)
}
}
# Continuation は、resume を 確実に1回だけ 呼び出す必要があるので、注意が必要です。
上記のコードを使うことで、以下のように、取得を待つようなコードを書くことが可能となります。
let retrievedData = await hkData()
以下のようにすることで、取得を待たないようなコードもかけます。
Task {
let retrievedData = await hkData()
}
processSomethingElse()
まとめ:HealthKit にアクセスする async/await 対応板
- save は、async 対応しているものを使う
- Query は、自分で async 対応させた関数を作る必要がある
- completionHandler をつかう Query を async 対応させるには、Continuation を使用する
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link