LoginSignup
72
70

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-03-27

この記事は、以下の資料を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を利用しています。特定のイベントで取得したい場合にはこれで十分です。
が、 HKStatisticsCollectionQuery では データ更新のハンドリングができ、取得時間範囲の指定にDateComponentが使えるので、こちらの方が便利な場合も多いです。

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から取得する歩数は、時間がかぶっていなければサードパーティのアプリや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)

サンプルコード

72
70
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
72
70