Healthkitでランニングデータを取得して監視する

ワークアウトの中の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 HKSampleQuery or HKAnchoredObjectQuery, to access the changes.

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を使う方法があるらしい。

😀

ABOUTこの記事をかいた人

個人アプリ開発者。Python、Swift、Unityのことを発信します。月間2.5万PVブログ運営。 Twitter:@yamagablog