🏃‍♂️

【iOS】歩数を取得する

2023/10/26に公開

歩数の取得としては、取得先としてイメージしやすいOS連携が可能なHealthKitからの取得と、HealthKitによらずiOSのシステム内部的に生成されてキャッシュされている歩数の取得の2つに分けられます。(他にもあったらすみません)

CMPedometer

An object for fetching the system-generated live walking data.

https://developer.apple.com/documentation/coremotion/cmpedometer

HealthKitによらずにシステム内部で生成されてキャッシュされている歩数のデータを取得するためのクラスが、CMPedometerになります。

取得の仕方はとても簡単です。

インスタンスメソッドであるqueryPedometerData(from:to:withHandler:)に開始日と終了日を与えることで、その期間の歩数のデータをwithHandler以下のクロージャで引数として受け取ることができます。

この際のデータはCMPedometerHandlerという型となりますが、これはエイリアスで実際には次のようにクロージャ型となっています。(というかHandlerなんだからクロージャか)

typealias CMPedometerHandler = (CMPedometerData?, Error?) -> Void

そのため、データとして利用しているのはCMPedometerData型になります。(と言っても、この型はシステムから受け取るのみで、直接インスタンスを生成したりする必要のあるものではないのでほとんど気にしなくていい)

例えば、引数名をdataとすると、次のように歩数を取り出せます。

guard let startDate = Calendar.current.date(
    byAdding: .day,
    value: -1,
    to: Date()
) else {
    return
}

CMPedometer().queryPedometerData(from: startDate, to: Date()) { (data, error) in
    guard let data = data, error == nil else { return }
    let _ = data.numberOfSteps.intValue //Int型の歩数
}

numberOfStepsプロパティが歩数にあたりますが、他にも秒間あたりの歩数(ケイデンス)なども取得できます。

https://developer.apple.com/documentation/coremotion/cmpedometerdata#1669412

HealthKit(HKStatisticsCollectionQuery)

HealthKitとの連携に関しては一旦省略します。

HealthKitと連携している前提で、歩数を取得するにはHKStatisticsCollectionQueryを利用します。取得に利用するクエリのためのクラスであり、そのクラス(クエリ)を実行するのはHKHealthStoreというクラスになります。HKは文字通り、HealthKitですね!

指定した期間や間隔での統計情報(平均、最大、最小、合計など)を取得するためのクエリを表すClassです。たとえば、過去1週間の毎日の歩数の合計を取得したい場合や、過去1ヶ月の毎週の心拍数の平均を取得したい場合などに利用します。

クラスのイニシャライザはinit(quantityType:quantitySamplePredicate:options:anchorDate:intervalComponents:)になります。

各パラメータは次のとおりです。

  • quantityType: どのデータタイプの統計を取得するかの指定(例:歩数、心拍数など)
  • quantitySamplePredicate: クエリの対象となるサンプルのフィルタリング条件
  • options: 取得する統計の種類を指定
  • anchorDate: 統計情報の集計を開始する基準日を指定
  • intervalComponents: 統計情報の集計間隔を指定

クエリの結果は、initialResultsHandlerstatisticsUpdateHandlerなどのハンドラを通じて取得します。このハンドラは、クエリの実行が完了したときや、統計情報に変更があったときに呼び出されます。

例えば次のような書き方が可能です

func fetchSteps(completion: @escaping (HKStatisticsCollection?) -> Void) {
        //必要なパラメータはすでに定義された値を参照しているとして
	let query = HKStatisticsCollectionQuery(
	    quantityType: stepType,
	    quantitySamplePredicate: predicate,
	    options: .cumulativeSum,
	    anchorDate: anchorDate,
	    intervalComponents: daily
	)

	query.initialResultsHandler = { query, statisticsCollection, error in
	    // コールバック
	    completion(statisticsCollection)
	}

	HKHealthStore().execute (query)
}

このようにして定義されたクエリの結果は、completion(statisticsCollection)で呼び出し元にコールバックされます。

渡されてきたHKStatisticsCollectionがクエリの実行結果です。
実行結果に対してenumerateStatisticsメソッドなどを利用することで、その統計値をさまざまな形で取得することができます。

たとえば、統計値に対してsumQuantity()を利用すると、期間内の合計値が得られます。

statisticsCollection
    .enumerateStatistics(from: startDate, to: endDate) { statistics, stop in
	    //期間内の合計を足し合わせた値
	    let count = statistics.sumQuantity()?.doubleValue(for: .count())
	}

HealthKitの方がいくらか処理が複雑には見えるものの、その分細かく統計値を利用することが可能であるようには思えます(いろいろ端折りました)

参考

  • CMPedometer

https://www.youtube.com/watch?v=vDTUuaGbvt8

https://github.com/azamsharp/PedometerSwiftUI

  • HealthKit

https://www.youtube.com/watch?v=ohgrzM9gfvM

https://github.com/azamsharp/SwiftUIHealthKit

Discussion