この記事はXcode6β4の時点でのHealthKitについて扱っています。 

HealthKit + PromiseKit事始め 基本的なデータの読み書き | Technology-Gym では、HealthKitを使った基本的な読み書きについて紹介しました。

単純に読み書きするだけでも様々なデータの型が用意されたり、データ共有もできるの使う意味はありますが、
さらにHealthKitにはHKStatisticsという統計(集計という方が近い?)処理するクラスが用意されています。 

HKStatisticsとHKStatisticsCollectionというクラスが統計処理に関係するものです、
それぞれ取得するのにHKStatisticsQueryとHKStatisticsCollectionQueryというのものを使います。 

以下の記事を先に読んでおくと理解しやすいでしょう。

このHKStatisticsとHKStatisticsQueryの対応関係は、前回の読み込みでやった
HKSampleとHKSampleQueryと全く同じです。 

HKQuantitySample

HKSampleQueryを叩いて、HKSampleの配列が結果として取得できるのと同じです。

 

ただし、SampleQueryと少し違い、HKStatisticsQueryの結果返ってくるのはひとつのHKStatisticsオブジェクトです。

HKStatisticsQueryを見てみるとコールバックのresultが配列じゃない事が分かります。

@interface HKStatisticsQuery : HKQuery

- (instancetype)initWithQuantityType:(HKQuantityType *)quantityType
             quantitySamplePredicate:(NSPredicate *)quantitySamplePredicate
                             options:(HKStatisticsOptions)options
                   completionHandler:(void(^)(HKStatisticsQuery *query, HKStatistics *result, NSError *error))handler;

@end

そのため、図にすると以下のような形ですね。

 

HKStatisticsQuery

HKStatisticsには最小値や最大値や平均値といった統計データが含まれているので、配列である必要がないのは当然といえば当然ですね。

HKStatisticsCollection

HKStatisticsについてはHealthKit 入門 2 – I’m Sei.を読むと大体使い方はわかると思います。
また、同時に平均値などは自分でHKSampleQueryを叩いた結果を使えば求められるし、そこまでメリットが見えないかもしれません。

HKStatisticsを活用するためにあるのがHKStatisticsCollectionです。

HKStatisticsCollectionは名前の通り、HKStatisticsを集めたものです(配列もあるが、グルーピングするというのが近い)

まずは実際にHKStatisticsCollectionを取得してみましょう。

サンプルはazu/StatisticsHealthKitにおいてあります。

azu/StatisticsHealthKit 

このサンプルではランダムな気温を追加して、それを折れ線グラフで表示する機能が入っています。
このグラフ作成にHKStatisticsCollectionが役に立つと思います。 

// return HKStatisticsCollection
- (PMKPromise *)collection:(HKQuantityType *) quantityType predicate:(NSPredicate *) predicate options:(HKStatisticsOptions) options anchorDate:(NSDate *) date components:(NSDateComponents *) components {
    return [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter) {
        HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options:options anchorDate:date intervalComponents:components];
        collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {
            if (!error) {
                fulfiller(result);
            } else {
                rejecter(error);
            }
        };
        [self.healthStore executeQuery:collectionQuery];
    }];
}

HKStatisticsCollectionはstartとendの日付を指定して、その間をどういう間隔で区切っていくかをNSDateComponentで指定します。(HKStatisticsOptionsはどの統計データを取得するのかのオプション) 

NSDateComponentでの指定は

が参考になります。

以下の例だとday = 1のNSDateComponentを作って渡しているので、1日間隔で統計処理されたものを取得できます。
(hour = 8なら8時間ごとというように、 NSDateComponentが示す時間ずつずらして行く感じ)

    NSDateComponents *dayComponents = [[NSDateComponents alloc] init];
    dayComponents.day = 1;
    NSDate *startOfMonth = [[NSDate date] dateAtStartOfMonth];
    NSDate *endOfMonth = [[NSDate date] dateAtEndOfMonth];
    PMKPromise *promise = [self.storeManager collection:self.managedType predicate:nil options:HKStatisticsOptionDiscreteAverage anchorDate:startOfMonth components:dayComponents];
    return promise.then(^(HKStatisticsCollection *collection) {
        NSMutableArray *graphDataListMutable = [NSMutableArray array];
        // first day -- last day
        [collection enumerateStatisticsFromDate:startOfMonth toDate:endOfMonth withBlock:^(HKStatistics *result, BOOL *stop) {
            [graphDataListMutable addObject:result];
        }];
        self.graphDataList = graphDataListMutable;
        return graphDataListMutable;
    });

このデータ集計の間隔を決められるというのがこのクラスの大きな点です。

HKStatisticsCollection

Introducing HealthKit から引用

このようにして作成したHKStatisticsCollectionQueryを使ってHKStatisticsCollectionを取得することが出来ます。

 

HKStatisticsCollectionQuery

HKStatisticsCollectionは単純にHKStatisticsの配列を持つ一方、
もう一つHKStatisticsを扱う方法を持っています。

それが上記の例でも使っているenumerateStatisticsFromDateによる間隔ごとの列挙です。

- (void)enumerateStatisticsFromDate:(NSDate *)startDate toDate:(NSDate *)endDate withBlock:(void(^)(HKStatistics *result, BOOL *stop))block;

この列挙の大きな特徴として、データがないグループも列挙されるという点があります。 
(- (NSArray *)statistics; でHKStatisticsを取得する場合はデータがあるグループのみを取得できます。)

例えば1日間隔でデータ集計していった時に、データそのものがない日も存在すると思いますが、
列挙した場合はその日もデータがないHKStatisticsとして取得できます。 

これがサンプルのazu/StatisticsHealthKitでも使っているグラフを1日毎に描画していく時に凄く便利な仕組みとなっています。

StatisticsHealthKit

通常のCoreDataなどから取得したものから1日ごとに区切った表示をする場合、データがない日の扱いにすごく困ると思います。

そのため、手動で1日ごとの区切りのデータを作りなおしたり、何らかの工夫をいれて1日ごとの区切りに見せる必要が出てきます。

しかし、HKStatisticsCollectionは単純に1日ごとの区切りをデータを列挙することができるので、この表示用のデータを作るという仕組みが省けます。

これは工夫すれば曜日別のデータ表示などにも活用できると思います。

これで、かなり大雑把ですがHKStatisticsCollectionについての説明は終わりです。

おわりに

HealthKitは多くのクラスがあって、どこから見ていくのかが難しい感じがしますが、
基本的に種類毎のQueryやクラスがあって、取得する際にやっていることは大体同じです。

APIの設計的にも結構良くできているなーという感じがするので、データを集計して処理するような仕組みを作る場合にも参考になることが多い気がします。

また、β4でHKWorkoutというトラッキング用のクラスなどが一気に増えたり追加が多いフレームワークな気がします。
(おそらく「iOS 8 beta3」からHealthアプリがM7モーションコプロセッサによるトラッキングに対応 | Linkmanの実装関係)


明日、2014年7月25日(金)に第1回 Tech-Gym byプラスアール @SMS 【iOS勉強会、開発者向け】 : ATNDというイベントで、Healthkitについて喋ります。

詳細はTech-GymというiOS勉強会を開催しますの方を見て下さい。 


Post Navigation