この記事は、以下の資料を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から取得する
HKSampleQuery
で取得する
HealthKitでは、いつ、何時に何歩歩いたかと言う情報をHealthStoreに保持しています。
HealthStoreからデータを取得する方法の一つにHKSampleQuery
を利用できます。
取得したデータの合計値を計算すれば良いかと考えられますが、この方法はやってはいけないダメな方法です。
なぜなら、HealthStoreにはiPhoneとApple Watchが自動記録した歩数データが入っているため、
素直に足し合わせてしまうと、二つの歩数計データを足し合わせてしまうことになります。
iPhoneだけでテストしてしまうと、一見正しい情報に見えてしまうため注意です。
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)
HKStatisticsQuery
/ HKStatisticsCollectionQuery
で取得する
多くの場合において、一つ一つの歩数を取得したいのではなく、データの特徴を知りたいはずです。
この場合は HKStatisticsQuery
/ HKStatisticsCollectionQuery
を利用しましょう。
以下のサンプルでは、HKStatisticsQuery
を利用しています。特定のイベントで取得したい場合にはこれで十分です。
が、 HKStatisticsCollectionQuery
では データ更新のハンドリングができ、取得時間範囲の指定にDateComponentが使えるので、こちらの方が便利な場合も多いです。
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から取得する歩数は、時間がかぶっていなければサードパーティのアプリやHealthAppから手動で登録した値も加算されます。
これでは、歩いた歩数がポイントになるアプリなどでは、歩いていないのに多くのポイントを入手することが可能になってしまいます。
HealthStoreに保存された各データには、登録したアプリのBundle Identifierのデータも一緒に書き込まれています。
よってこれでフィルタリングすれば、iPhone、AppleWatchの自動歩数記録機能から保存されたデータのみを抽出することができます。
自動歩数記録機能で記録されたデータとHealthAppのBundle Identifierは以下です。
- 自動歩数記録機能:
com.apple.health.[Device ID]
(Device ID部分は端末によって変わります) - 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)
サンプルコード