Posted at

iOSで正しく歩数を取得する

この記事は、以下の資料をqiita用の記事にしたものです。

https://speakerdeck.com/satoshin/getting-step-count-on-ios


はじめに

iOSで歩数を取得する方法には以下の二つの方法があります。


  • CMPedometerから取得する方法

  • HealthKitから取得する方法

HealthKitの方では、取得方法を間違えると実際のデータと大きく異なるので注意してください。


CMPedometerから取得する

let pedometer = CMPedometer()

pedometer.queryPedometerData(from: start, to: end) { (data, error) in
print(data?.numberOfSteps)
}


  • start ~ end までの間で端末で検知した歩数を取得


    • AppleWatchの情報取得はWatchAppで実装する必要あり



  • 取得できるのは過去7日分


HealthKitから取得する


:warning: HKSampleQuery で取得する

HealthKitでは、いつ、何時に何歩歩いたかと言う情報をHealthStoreに保持しています。

HealthStoreからデータを取得する方法の一つにHKSampleQueryを利用できます。

取得したデータの合計値を計算すれば良いかと考えられますが、この方法はやってはいけないダメな方法です。

なぜなら、HealthStoreにはiPhoneApple Watchが自動記録した歩数データが入っているため、

素直に足し合わせてしまうと、二つの歩数計データを足し合わせてしまうことになります。

iPhoneだけでテストしてしまうと、一見正しい情報に見えてしまうため注意です。


間違った取得方法-HKSampleQueryで取得した合計値を表示する

let store = HKHealthStore()

let type = HKSampleType.quantityType(forIdentifier: .stepCount)!
let predicate = HKQuery.predicateForSamples(withStart: start, end: end)
let query = HKSampleQuery(sampleType: type,
predicate: predicate,
limit: HKObjectQueryNoLimit,
sortDescriptors: nil) { (query, result, error) in
let result = result as! [HKQuantitySample]
let sum = result(0) { $0 + $1.quantity.doubleValue(for: .count()) }
print(sum)
}
store.execute(query)


:o: HKStatisticsQuery / HKStatisticsCollectionQuery で取得する

多くの場合において、一つ一つの歩数を取得したいのではなく、データの特徴を知りたいはずです。

この場合は HKStatisticsQuery / HKStatisticsCollectionQuery を利用しましょう。

以下のサンプルでは、HKStatisticsQueryを利用していますが、

実際には update のハンドリングができ、時間範囲にDateComponentが使える、

HKStatisticsCollectionQueryを利用することが多いです。


HKStatisticsQueryで歩数を取得する

let store = HKHealthStore()

let type = HKSampleType.quantityType(forIdentifier: .stepCount)!
let predicate = HKQuery.predicateForSamples(withStart: start, end: end)
let query = HKStatisticsQuery(quantityType: type,
quantitySamplePredicate: predicate,
options: .cumulativeSum) { (query, statistics, error) in
print(statistics?.sumQuantity())
}
store.execute(query)


不正対策

HealthKitから取得する歩数は、時間がかぶっていなければThirdpartyやHealthAppから手動で登録した値も加算されます。

これでは、歩いた歩数がポイントになるアプリなどでは、歩いていないのに多くのポイントを入手することが可能になってしまいます。

HealthStoreに保存された各データには、登録したアプリのBundle Identifierのデータも一緒に書き込まれています。

よってこれでフィルタリングすれば、iPhone、AppleWatchの自動歩数記録機能から保存されたデータのみを抽出することができます。

自動歩数記録機能で記録されたデータとHealthAppのBundle Identifierは以下です。


  • 自動歩数記録機能: com.apple.health.[UUID] (UUID部分は端末によって変わります)

  • HealthApp: com.apple.Health

よって、hasPrefix("com.apple.health")で判定してあげればOKです。


自動記録された歩数だけを取得する

let store = HKHealthStore()

let type = HKSampleType.quantityType(forIdentifier: .stepCount)!
let datePredicate = HKQuery.predicateForSamples(withStart: start, end: end)
let query = HKStatisticsQuery(quantityType: type,
quantitySamplePredicate: datePredicate,
options: .separateBySource)
{ (query, data, error) in
if let sources = data?.sources?.filter({ $0.bundleIdentifier.hasPrefix("com.apple.health") }) {
let sourcesPredicate = HKQuery.predicateForObjects(from: Set(sources))
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, sourcesPredicate])
let query = HKStatisticsQuery(quantityType: type,
quantitySamplePredicate: predicate,
options: .cumulativeSum)
{ (query, statistics, error) in
print(statistics?.sumQuantity())
}
store.execute(query)
}
}
store.execute(query)


サンプルコード

https://github.com/sato-shin/ios-steps