ワークアウトの中の1つであるランニングデータを取得する&監視するための方法について。
`HKObserverQuery`を実行すると、初回にコールバックが実行されるのでアプリ起動時などの初期のデータ取得に使える。
さらに、定期的にワークアウトデータの監視もしてくれる。いちいちユーザさんが更新ボタンを押す必要がない。
let workouttype = HKWorkoutType.workoutType() func startObserving() { let observerQuery = HKObserverQuery(sampleType: workouttype, predicate: nil, updateHandler: { _, _, error in if error != nil { return } self.readWorkOut() }) healthStore.execute(observerQuery) }
コールバックの中の処理はどうするのかというと、以下の通り`HKsampleQuery`を実行する。
func readWorkOut(){ let sampleQuery = HKSampleQuery(sampleType: workouttype, predicate: nil, limit: 0, sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)], //新しい順に並べる resultsHandler: {(_,sample,error) in if error != nil { return } self.recordWorkout(sample: sample as? [HKWorkout]) }) healthStore.execute(sampleQuery) }
ワークアウトのデータは、sampleとして返ってくる。
HKObserverクエリーの中でHKSampleクエリーを実行しているのでなんだか不思議な感じがするが、このやり方はAppleの記事でも推奨さている方法である。
The observer query’s update handler does not receive any information about the change—just that a change occurred. You must execute another query, for example an
HKSample
orQuery HKAnchored
, to access the changes.Object Query
https://developer.apple.com/documentation/healthkit/hkobserverquery/executing_observer_queries
サンプルデータを処理するコードは以下の通り。
要はランニングの時間や距離を配列に入れているだけである。
func recordWorkout(sample:[HKWorkout]?) { DispatchQueue.main.async { self.workoutRecord = [] self.isWorkoutLoading = true } if let workouts = sample{ for workout in workouts{ //ランニングに限定する if (workout.workoutActivityType == .running) { guard let distance = workout.totalDistance else { return } guard let energy = workout.totalEnergyBurned else { return } let distanceFormatted = (distance.doubleValue(for: HKUnit.meterUnit(with: .kilo)) * 100).rounded() / 100 let energyInt = Int(energy.doubleValue(for: HKUnit.kilocalorie()).rounded()) let row = WorkOutRecord(startTime: workout.startDate, endTime: workout.endDate, distance: distanceFormatted, energyBurned: energyInt, interval: workout.duration) DispatchQueue.main.async { self.workoutRecord.append(row) self.RunningDict[workout.startDate.DateToStringFormatter(format: "yyyy-MM-dd")] = true } } } } DispatchQueue.main.async { self.isWorkoutLoading = false self.lastupdateWorkout = Date() } }
上記のコードでは、全ワークアウトデータを毎回取得するようにしている。
以下の記事が詳しいが、クエリーを実行した時間を覚えておいて、変化したデータだけ取得するやり方もあるようだ。
https://dmtopolog.com/healthkit-changes-observing/
その場合には、predicateで期間指定する方法やHKAnchoredObjectQueryを使う方法があるらしい。
😀