HealthKit的使用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
HealthKit的使⽤
⼀、项⽬中关联HealthKit框架
1.在Capabilities选项中打开HealthyKit选项
HealthKit关联路径
⾸先填写好你项⽬的Bundle Identifier并且选好Team(这两个东西最好事先设置好,以免之后⼜得重新关联),然后在项⽬物理⽂件结构中点选对应的项⽬,在TARGETS中选
择你⾃⾝的项⽬,再在右侧选择Capabilities选项,选择开启HeathyKit选项
图中有4个选项,但是你的可能缺少其中的选项,这个时候可以根据其中缺少的去配置
2.导⼊HealthyKit.framework
在Build Phases中导⼊HealthyKit.framework库
3.确认你的App ID中HealthyKit选项是可⽤状态
4.配置plist⽂件
当你做好上⾯4步之后,你的HealthyKit选项就会与与1中的图⼀致有4个选项,那么所有的准备⼯作就完成了。
这时候我们⼯程下⾯会有⼀个.entitlements⽂件,⽂件内容如下:
这个⽂件最好还是检查⼀下,个⼈曾踩坑,这个是⾃动⽣成的⽂件,所以没有检查,以致于所有步骤都做了,⽅法也调⽤了,但是App却⼀直不能获取HealthyKit权限,也⼀直不
能获取数据。
⼆、项⽬中使⽤HealthKit
1、HealthKit所⽀持的系统和设备
因为HealthKit框架是在iOS8系统出来之时⼀同推出的,所以该框架⽬前只⽀持iOS8及以上系统,⽬前⽀持的设备有iPhone、iWatch,要记得iPad是不⽀持的哦,如果你的代码
同时⽀持iPhone和iPad设备,那么记得判断下设备还有系统版本号,以免出现不必要的奔溃现象。
在项⽬中导⼊后,你也可以使⽤以下代码判断该设备的系统能否使⽤健康数
据:
[HKHealthStore isHealthDataAvailable]
2、应⽤授权
要想获取健康数据中的步数,则需要通过⽤户许可才⾏。
具体可以使⽤以下代码进⾏授权:
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
NSSet *readObjectTypes = [NSSet setWithObjects:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount], nil];
[healthStore requestAuthorizationToShareTypes:nil readTypes:readObjectTypes completion:^(BOOL success, NSError *error) {
if (success == YES) {
//授权成功
} else {
//授权失败
}
}];
这⾥调⽤了requestAuthorizationToShareTypes: readTypes: completion:⽅法,⽤于对应⽤授权需要获取和分享的健康数据:
1、第⼀个参数传⼊⼀个NSSet类型数据,⽤于告知⽤户,我的app可能会在你的健康数据库中修改这些选项数据(显然⽬前我们不需要,传nil)
2、第⼆个参数也是传⼊NSSet类型数据,告知⽤户,我的app可能会从你的数据库中读取以下⼏项数据
3、第三个是授权许可回调,BOOL值success⽤于区分⽤户是否允许应⽤向数据库存取数据
3、获取健康步数
授权完成之后,我们接下来就可以调⽤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 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、最后⼀⾏代码⽤于执⾏数据查询操作
通过这段代码获取的数据,打印出来会发现,它获取的是⽐较详尽的数据,精确到每⼀⼩段时间从开始时间到结束时间内所获取的步数。
4、数据采集
有时候需求并不需要了解这么详尽的数据,只希望获取每⼩时、每天或者每⽉的步数,那么我们就需要⽤到另⼀个新类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 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 ([ 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、最后⼀⾏执⾏该查询统计操作执⾏这段代码,通过打印的⽇志可以看到当前设备中存储的按⽇间隔存储的步⾏数总和了。
三、HealthKit⼯具类
EBHealthKitUtils.h
#import <Foundation/Foundation.h>
#import <HealthKit/HealthKit.h>
@interface EBHealthKitUtils : NSObject
+(id)shareInstance;
- (void)getPermissions:(void(^)(BOOL success))Handle;
/*!
* @author lei
*
* @brief 获取当天实时步数
*
* @param handler 回调
*/
- (void)getRealTimeStepCountCompletionHandler:(void(^)(double value, NSError *error))handler;
/*!
* @author lei
*
* @brief 获取当天所爬楼层
*
* @param handler 回调
*/
- (void)getRealTimeFloorCountCompletionHandler:(void(^)(double value, NSError *error))handler;
/*!
* @author lei
*
* @brief 获取⼀定时间段步数
*
* @param predicate 时间段
* @param handler 回调
*/
- (void)getStepCount:(NSPredicate *)predicate completionHandler:(void(^)(double value, NSError *error))handler;
/*!
* @author lei
*
* @brief 获取卡路⾥
*
* @param predicate 时间段
* @param quantityType 样本类型
* @param handler 回调
*/
- (void)getKilocalorieUnit:(NSPredicate *)predicate quantityType:(HKQuantityType*)quantityType completionHandler:(void(^)(double value, NSError *error))handler;
/*!
* @author lei
*
* @brief 获取当天某种类型的运动卡路⾥
*
* @param predicate 时间段
* @param quantityType 样本类型
* @param handler 回调
*/
- (void)getKilocalorieUnitWithQuantityType:(HKQuantityType*)quantityType completionHandler:(void(^)(double value, NSError *error))handler;
/*!
* @author lei
*
* @brief 当天时间段
*
* @return
*/
+ (NSPredicate *)predicateForSamplesToday;
/*!
* @author lei
*
* @brief 当天距离
*
* @return
*/
- (void)getDistanceWalkingRunningCompletionHandler:(void(^)(double value, NSError *error))handler;
@end
EBHealthKitUtils.m
#import "EBHealthKitUtils.h"
#import <UIKit/UIDevice.h>
#import <PPModuleCommonLib/LMBPPConstants.h>
#import "ElectronicBalancePch.pch"
#import "ElectronicBalanceApiDefine.h"
#import "HKHealthStore+AAPLExtensions.h"
@interface EBHealthKitUtils ()
@property (nonatomic, strong) HKHealthStore *healthStore;
@end
#define HKVersion [[[UIDevice currentDevice] systemVersion] doubleValue]
#define CustomHealthErrorDomain @"com.apple.healthkit"
@implementation EBHealthKitUtils
+(id)shareInstance
{
static id manager ;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[[self class] alloc] init];
});
return manager;
}
- (void)getPermissions:(void(^)(BOOL success))Handle
{
if(HKVersion >= 8.0)
{
if ([HKHealthStore isHealthDataAvailable]) {
if(self.healthStore == nil)
self.healthStore = [[HKHealthStore alloc] init];
/*
组装需要读写的数据类型
*/
NSSet *writeDataTypes = [self dataTypesToWrite];
NSSet *readDataTypes = [self dataTypesRead];
/*
注册需要读写的数据类型,也可以在“健康”APP中重新修改
*/
[self.healthStore requestAuthorizationToShareTypes:writeDataTypes readTypes:readDataTypes completion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(@"%@\n\n%@",error, [error userInfo]);
return ;
}
else
{
Handle(YES);
}
}];
}
}
}
- (NSSet *)dataTypesToWrite
{
return [NSSet set];
}
- (NSSet *)dataTypesRead
{
HKQuantityType *activeEnergyType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
// 注释掉暂不使⽤的内容
/*
HKQuantityType *heightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
HKQuantityType *weightType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
HKQuantityType *temperatureType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyTemperature];
HKCharacteristicType *birthdayType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
HKCharacteristicType *sexType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
*/
HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKQuantityType *floorCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed];
HKQuantityType *WalkingRunningType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
// return [NSSet setWithObjects:heightType, temperatureType,birthdayType,sexType,weightType,stepCountType, activeEnergyType,nil];
return [NSSet setWithObjects:stepCountType,floorCountType,activeEnergyType,WalkingRunningType, nil];
}
- (void)getDistanceWalkingRunningCompletionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey]; NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:sampleType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
if (error) {
// Perform Proper Error Handling Here...
NSLog(@"*** An error occured while setting up the stepCount observer. %@ ***",
error.localizedDescription);
handler(0,error);
// abort();
}
HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
[self.healthStore aapl_mostRecentQuantitySampleOfType:stepType
predicate:[EBHealthKitUtils predicateForSamplesToday]
completion:^(NSArray *results, NSError *error) {
if(error)
{
handler(0,error);
}
else
{
double meter = 0;
for(HKQuantitySample *quantitySample in results)
{
HKQuantity *quantity = quantitySample.quantity;
HKUnit *meterUnit = [HKUnit meterUnit];
double value = [quantity doubleValueForUnit:meterUnit];
meter += value;
}
handler(meter, error);
}
}];
}];
[self.healthStore executeQuery:query];
}
}];
}
}
- (void)getRealTimeStepCountCompletionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey]; NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:sampleType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
if (error) {
// Perform Proper Error Handling Here...
NSLog(@"*** An error occured while setting up the stepCount observer. %@ ***",
error.localizedDescription);
handler(0,error);
// abort();
}
[self getStepCount:[EBHealthKitUtils predicateForSamplesToday] completionHandler:^(double value, NSError *error) {
handler(value,error);
}];
}];
[self.healthStore executeQuery:query];
}
}];
}
}
- (void)getStepCount:(NSPredicate *)predicate completionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey]; NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
[self.healthStore aapl_mostRecentQuantitySampleOfType:stepType predicate:predicate completion:^(NSArray *results, NSError *error) {
if(error)
{
handler(0,error);
}
else
{
NSInteger totleSteps = 0;
for(HKQuantitySample *quantitySample in results)
{
HKQuantity *quantity = quantitySample.quantity;
HKUnit *heightUnit = [HKUnit countUnit];
double usersHeight = [quantity doubleValueForUnit:heightUnit];
totleSteps += usersHeight;
}
NSLog(@"当天⾏⾛步数 = %ld",(long)totleSteps);
if(results.count == 0) {
if(results.count == 0){
totleSteps = [[kUserDefaults objectForKey:kEBStepCountsKey]integerValue];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[kUserDefaults setObject:[NSString stringWithFormat:@"%zd",totleSteps] forKey:kEBStepCountsKey];
[kUserDefaults synchronize];
handler(totleSteps,error);
});
}
}];
}
}];
}
}
+ (NSPredicate *)predicateForSamplesToday {
if(HKVersion >= 8.0)
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
[components setHour:0];
[components setMinute:0];
[components setSecond: 0];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
return predicate;
}
else
return nil;
}
- (void)getKilocalorieUnit:(NSPredicate *)predicate quantityType:(HKQuantityType*)quantityType completionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKStatisticsQuery *query = [[HKStatisticsQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum completionHandler:^(HKStatisticsQuery *query, HKStatistics *re NSLog(@"health = %@",result);
HKQuantity *sum = [result sumQuantity];
double value = [sum doubleValueForUnit:[HKUnit kilocalorieUnit]];
NSLog(@"%@卡路⾥ ---> %.2lf",sum,value);
if(handler)
{
handler(value,error);
}
}];
[self.healthStore executeQuery:query];
}
}];
}
}
- (void)getKilocalorieUnitWithQuantityType:(HKQuantityType*)quantityType completionHandler:(void(^)(double value, NSError *error))handler {
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKStatisticsQuery *query = [[HKStatisticsQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:[EBHealthKitUtils predicateForSamplesToday] options:HKStatisticsOptionCumulativeSum completionHandler:^(HKS NSLog(@"health = %@",result);
HKQuantity *sum = [result sumQuantity];
double value = [sum doubleValueForUnit:[HKUnit kilocalorieUnit]];
NSLog(@"%@卡路⾥ ---> %.2lf",sum,value);
if(handler)
{
handler(value,error);
}
}];
[self.healthStore executeQuery:query];
}
}];
}
}
- (void)getRealTimeFloorCountCompletionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKSampleType *sampleType =
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:sampleType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
if (error) {
// Perform Proper Error Handling Here...
NSLog(@"*** An error occured while setting up the stepCount observer. %@ ***",
error.localizedDescription);
handler(0,error);
// abort();
}
[self getFloorCount:[EBHealthKitUtils predicateForSamplesToday] completionHandler:^(double value, NSError *error) {
handler(value,error);
}];
}];
[self.healthStore executeQuery:query];
}
}];
}
}
- (void)getFloorCount:(NSPredicate *)predicate completionHandler:(void(^)(double value, NSError *error))handler
{
if(HKVersion < 8.0)
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:CustomHealthErrorDomain code:0 userInfo:userInfo];
handler(0,aError);
}
else
{
[self getPermissions:^(BOOL success) {
if(success)
{
HKQuantityType *stepType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed];
[self.healthStore aapl_mostRecentQuantitySampleOfType:stepType predicate:predicate completion:^(NSArray *results, NSError *error) { if(error)
{
handler(0,error);
}
else
{
NSInteger totleSteps = 0;
for(HKQuantitySample *quantitySample in results)
{
HKQuantity *quantity = quantitySample.quantity;
HKUnit *heightUnit = [HKUnit countUnit];
double usersHeight = [quantity doubleValueForUnit:heightUnit];
totleSteps += usersHeight;
}
NSLog(@"当天所爬的楼层 = %ld",(long)totleSteps);
if(results.count == 0){
totleSteps = [[kUserDefaults objectForKey:kEBFloorCountsKey]integerValue];
}
dispatch_async(dispatch_get_main_queue(), ^{
[kUserDefaults setObject:[NSString stringWithFormat:@"%zd",totleSteps] forKey:kEBFloorCountsKey];
[kUserDefaults synchronize];
handler(totleSteps,error);
});
}
}];
}
}];
}
}
@end。