最近在做项目的时候,突然接到产品经理给的一个需求,要求在项目中统计用户每日步数,具体要求就是获取手机健康应用中统计的每日步数,然后以图形方式显示。
刚听到这个需求的时候就想到了苹果在iOS8系统推出之时顺带出的HealthKit框架,不过对于其API倒是非常的陌生。于是就开始在网上查找资料,百度,谷歌一番之后,发现也未能找到一个资料能够很好地引导新手使用该框架(可能是我没找到好的-_-!),经过几天的个人摸索,也算是有点心得体会,在这里就将自己对该框架相关的使用做个梳理,也希望能够为接下来即将接触和使用该框架的童鞋提供一点小小的帮助。如有什么疑问或者不对的地方欢迎提出。以下内容以获取步数为例,其他数据获取可以此类推。
一、项目中关联HealthKit框架
首先填写好你项目的Bundle Identifier并且选好Team(这两个东西最好事先设置好,以免之后又得重新关联),然后在项目物理文件结构中点选对应的项目,在TARGETS中选择你自身的项目,再在右侧选择Capabilities选项
从中找到HealthKit这项,点击右侧的开关开启,当出现中间红框所示的内容,表示项目与HealthKit框架关联成功了
你会在项目中看到多了HealthKit.framework和.entitlements结尾的这两个文件,OK一切顺利,接下来就可以Code了。
二、HealthKit所支持的系统和设备
因为HealthKit框架是在iOS8系统出来之时一同推出的,所以该框架目前只支持iOS8及以上系统,目前支持的设备有iPhone、iWatch,要记得iPad是不支持的哦,如果你的代码同时支持iPhone和iPad设备,那么记得判断下设备还有系统版本号,以免出现不必要的奔溃现象。在项目中导入<HealthKit/HealthKit.h>
后,你也可以使用以下代码判断该设备的系统能否使用健康数据:
[HKHealthStore isHealthDataAvailable]
三、应用授权
要想获取健康数据中的步数,则需要通过用户许可才行。具体可以使用以下代码进行授权:
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
NSSet *readObjectTypes = [NSSet setWithObjects:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount], nil];
[healthStore requestAuthorizationToShareTypes:nil readTypes:readObjectTypes completion:^(BOOL success, NSError *error) {
//进行一些操作
}];
这里调用了requestAuthorizationToShareTypes: readTypes: completion:
方法,用于对应用授权需要获取和分享的健康数据:
1、第一个参数传入一个
NSSet
类型数据,用于告知用户,我的app可能会在你的健康数据库中修改这些选项数据(显然目前我们不需要,传nil)
2、第二个参数也是传入NSSet
类型数据,告知用户,我的app可能会从你的数据库中读取以下几项数据
3、第三个是授权许可回调,这里的BOOL值success
不能用于区分用户是否允许应用向数据库存取数据,这一点评论区有人提到,我也对此进行了测试,发现确实即使用户不允许,该值也为YES
四、获取健康步数
授权完成之后,我们接下来就可以调用API来获取数据库数据了。
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:nil endDate:nil options:HKQueryOptionStrictStartDate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[sortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if(!error && results) {
for(HKQuantitySample *samples in results) {
NSLog(@"%@ 至 %@ : %@", samples.startDate, samples.endDate, samples.quantity);
}
} else {
//error
}
}];
[healthStore executeQuery:sampleQuery];
这段代码主要做了以下几件事情:
1、第一段通过传入一个枚举值HKQuantityTypeIdentifierStepCount来创建一个样品类的实例,用于告知,我接下来要获取的数据是步数>2、第二段代码通过创建一个NSPredicate类的实例,用于获取在某个时间段的数据,这里startDate和endDate传入nil,表示获取全部数据,第三个参数传入一个Option,里面有三个值,这个参数我试验了下不同的值代入,发现返回的结果都是一样的,要是有谁知道这个值是做什么用的麻烦告知我一声~
3、第三段代码创建了一个NSSortDescriptor类实例,用于对查询的结果排序
4、第四段代码通过调用HKSampleQuery类的实例方法获取所需数据
5、最后一行代码用于执行数据查询操作
通过这段代码获取的数据,打印出来会发现,它获取的是比较详尽的数据,精确到每一小段时间从开始时间到结束时间内所获取的步数。
五、数据采集
有时候需求并不需要了解这么详尽的数据,只希望获取每小时、每天或者每月的步数,那么我们就需要用到另一个新类HKStatisticsCollectionQuery
进行数据的分段采集
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = 1;
HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:0] intervalComponents:dateComponents];
collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {
for (HKStatistics *statistic in result.statistics) {
NSLog(@"\n%@ 至 %@", statistic.startDate, statistic.endDate);
for (HKSource *source in statistic.sources) {
if ([source.name isEqualToString:[UIDevice currentDevice].name]) {
NSLog(@"%@ -- %f",source, [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]]);
}
}
}
};
[healthStore executeQuery:collectionQuery];
1、第一段代码所做的和之前的一样,定义需要获取的数据为步数
2、第二段代码创建一个NSDateComponents类实例,设置我要获取的步数时间间隔,这里设置为按天统计,这里也可以设置按小时或者按月统计
3、第三段代码创建查询统计对象collectionQuery
,通过传入四个参数进行初始化:
1、第一个参数同上面一样,设置需要查询的类型
2、第二个参数传入一个NSPredicate实例,目前这里传nil
3、第三个参数是关键,传入一个Option可选值,告诉查询统计对象我需要获取的是啥,这里传入HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource
值,获取时间段的步数和以及将数据根据不同的数据来源进行分段
4、第四个参数传入一个锚点,类似于数组的索引值,查询将会从改锚点开始查询,这里可以根据不同的锚点值,获取日/周/月/年数据
4、第四段代码是将collectionQuery
对象的block属性initialResultsHandler
进行赋值,该block会在数据查询成功之后进行回调,从中可以获得我们想要的数据
5、最后一行执行该查询统计操作执行这段代码,通过打印的日志可以看到当前设备中存储的按日间隔存储的步行数总和了。
返回的数据中HKSource
对象中的name
可用于区分健康数据来源,一般只获取设备中的步数,过滤其他第三方数据来源,目前微信、QQ所用的记步就是区分了不同的数据来源,防止作弊!!!
最后附上一个仿健康应用步数的Demo:
gitHub地址:https://github.com/MarsCWD/HealthKitDemo
参考链接:
http://vit0.com/blog/2014/10/30/ios-8-healthkit-jie-shao/
https://segmentfault.com/a/1190000003779081