[iOS 8] CMPedometer で歩数を取得する

歩数計機能

iOS 8 の Core Motion フレームワークに CMPedometer という歩数関連のデータを取得することができる API が追加されました。このクラスを使うと歩数/走行距離/昇り降りしたフロアの数などを計測できたり、過去に遡ってデータを参照できたりします。

なお、iOS 7 で追加された CMStepCounter がありますが、これの機能増強版と言えます。CMStepCounter は Deprecated になりました。短命でしたね。。

NS_CLASS_DEPRECATED_IOS(7_0,8_0,"Use CMPedometer instead")
@interface CMStepCounter : NSObject

この API は iPhone 5s に搭載されているモーション・コプロセッサー「M7」以降を利用して歩数を計測するので、電池消費量がかなり少ないです。

CMPedometer を使ってみよう

ということで早速使ってみましょう。まずプロジェクトを作成し Core Motion フレームワークを追加します。

pedometer01

モニタリング

あとはもう実装だけです。まずはモニタリングを行ってみましょう。

// メンバー変数でないと動作しないので注意
let pedometer = CMPedometer()

func startStepCounting() {
    // CMPedometerが利用できるか確認
    if CMPedometer.isStepCountingAvailable() {
        // 計測開始
        pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: {
            [unowned self] data, error in
            dispatch_async(dispatch_get_main_queue(), {
                println("update")
                if error != nil {
                    // エラー
                    self.label.text = "エラー : \(error)"
                    println("エラー : \(error)")
                } else {
                    let lengthFormatter = NSLengthFormatter()
                    // 歩数
                    let steps = data.numberOfSteps
                    // 距離
                    let distance = data.distance.doubleValue
                    // 速さ
                    let time = data.endDate.timeIntervalSinceDate(data.startDate)
                    let speed = distance / time
                    // 上った回数
                    let floorsAscended = data.floorsAscended
                    // 降りた回数
                    let floorsDescended = data.floorsDescended
                    // 結果をラベルに出力
                    self.label.text = "Steps: \(steps)"
                        + "\n\nDistance : \(lengthFormatter.stringFromMeters(distance))"
                        + "\n\nSpeed : \(lengthFormatter.stringFromMeters(speed)) / s"
                        + "\n\nfloorsAscended : \(floorsAscended)"
                        + "\n\nfloorsDescended : \(floorsDescended)"
                }
            })
        })
    }
}

まずはじめに CMPedometer#isStepCountingAvailable を呼びます。歩数測定ができるデバイス (= M7) のときに true が返ります。測定可能な場合のみ処理を行うようにしましょう。

次に CMPedometer インスタンスを生成し、CMPedometer#startPedometerUpdatesFromDate:withHandler を呼びます。

第一引数は、今までのデータを取得できるように NSDate が指定できるようになっています。メソッドのコール時から計測したい場合は NSDate を生成して渡します。第二引数にはハンドラを渡します。データは CMPedometerData オブジェクトとして渡されます。新しいデータが追加されると、ハンドラがバックグラウンドで繰り返し呼び出されます。別スレッドで呼ばれるので、この中で View の更新などを行いたい場合はメインスレッドで処理しましょう。

実行します。Core Motion フレームワークの API を利用するにはユーザーのプライバシー許可が必要になるため、初回実行時は次のようなダイアログが表示されます。

pedometer02

許可すると、歩数や距離、速さが取得できます。

pedometer03

データの参照

次に過去のデータを参照してみましょう。

func showHistory() {
    // CMPedometerが利用できるか確認
    if CMPedometer.isStepCountingAvailable() {
        // この日から
        let fromDate = NSDate(timeIntervalSinceNow: -60 * 60 * 24 * 7)
        // この日まで
        let toDate = NSDate()
        pedometer.queryPedometerDataFromDate(fromDate, toDate: toDate, withHandler: {
            [unowned self] data, error in
            dispatch_async(dispatch_get_main_queue(), {
                println("update")
                if error != nil {
                    // エラー
                    self.label.text = "エラー : \(error)"
                    println("エラー : \(error)")
                } else {
                    let lengthFormatter = NSLengthFormatter()
                    // 歩数
                    let steps = data.numberOfSteps
                    // 距離
                    let distance = data.distance.doubleValue
                    // 速さ
                    let time = data.endDate.timeIntervalSinceDate(data.startDate)
                    let speed = distance / time
                    // 上った回数
                    let floorsAscended = data.floorsAscended
                    // 降りた回数
                    let floorsDescended = data.floorsDescended
                    // 結果をラベルに出力
                    self.label.text = "Steps: \(steps)"
                        + "\n\nDistance : \(lengthFormatter.stringFromMeters(distance))"
                        + "\n\nSpeed : \(lengthFormatter.stringFromMeters(speed)) / s"
                        + "\n\nfloorsAscended : \(floorsAscended)"
                        + "\n\nfloorsDescended : \(floorsDescended)"
                }
            })
        })
    }
}

過去のデータを取得するには CMPedometer#queryPedometerDataFromDate:toDate:withHandler を呼びます。第一引数、第二引数には取得したい期間を指定し、第三引数には取得結果のコールバックのハンドラを渡します。取得できた場合、モニタリング時と同様 CMPedometerData オブジェクトとして取得できます。

実行すると次のように、期間内の歩数や距離などが取得できます。

pedometer04

まとめ

歩数計機能は CMStepCounter から更に機能が上がっています。歩数などの健康に関する情報は HealthKit でも扱えるようになったので、ユーザーにより身近な存在になったと言えるでしょう。ぜひアプリで活用したいですね。

参考