iOS开发技能IT技术iOS开发技术

iOS开发-关于苹果健康数据的获取

2016-08-25  本文已影响2501人  Mister志伟
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'NSHealthUpdateUsageDescription must be set in the app's Info.plist
in order to request write authorization.'

处理方法:在Info.plist文件中添加关键字NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription,写入和读取都需要。iOS10对于涉及用户隐私的很多数据获取都添加了类似的设置,可根据提示在plist文件中添加相应的权限字段。

在苹果的 iOS8 系统中自带了健康应用,可以记录我们在一天当中的运动数据,比如走了多少步,一天走了或跑了多少公里等。还可以接入硬件设备以写入运动数据,HealthKit框架为苹果为采集各种健康数据源提供的接口,可以用来采集和整合各种健康数据的来源,如运动手环,AppleWatch,以及iPhone设备提供的健康数据。这里简单介绍下如何获取苹果健康的数据。
实现流程:
一、在工程中添加HealthKit库。
二、创建模型(或在其他模型中添加#import),添加HKHealthStore属性,HKHealthStore是使用HealthKit中关键的一个类。
三、创建调用方法,在方法实现中主要要做的有3步:

1、判断设备是否支持HealthKit框架

2、请求苹果健康的认证

3、获取苹果健康的数据

*// 方法代码

- (void)getIphoneHealthData{

self.healthSteps = [NSMutableArray array];

self.healthDistances = [NSMutableArray array];

self.healthCalories = [NSMutableArray array];

NSSet *getData;

// 1.判断设备是否支持HealthKit框架

if ([HKHealthStore isHealthDataAvailable]) {

getData = [self getData];

} else {

NSLog(@"---------不支持 HealthKit 框架");

}

store = [[HKHealthStore alloc] init];

// 2.请求苹果健康的认证

[store requestAuthorizationToShareTypes:nil readTypes:getData completion:^(BOOL success, NSError * _Nullable error) {

if (!success) {

NSLog(@"--------请求苹果健康认证失败");

return ;

}

dispatch_async(dispatch_get_main_queue(), ^{

// 3.获取苹果健康数据

[self getHealthStepData];

[self getHealthDistanceData];

});

}];

}

在第一步判断设备支持HealthKit框架后,设置好要获取的数据类型,笔者此处获取的是步数,距离数据。

*// getData方法


- (NSSet *)getData{

HKQuantityType  *step = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

HKQuantityType *distance = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];

return [NSSet setWithObjects:step,distance, nil];

}

第二步请求苹果健康的认证,此处用到HKHealthStore属性,实例化属性后调用认证方法,注意方法中的参数ShareTypes 和 readTypes,一个是认证写入类型,一个是认证读取类型,认证成功后开始最重要的第三步,获取数据,此处为异步执行。

在获取数据的方法中时间的设置是比较麻烦的,不过都是一样的;HKQuantityType实例对象用于设置要获取的数据类型,NSPredicate实例对象用于设置获取数据的时间段,HKStatisticsCollectionQuery实例对象用于获取数据,在获取的数据结果中需要把每一条数据遍历出来,可以做一些需要的格式的处理。

*// 代码


- (void)getHealthStepData{

HKHealthStore *healthStore = [[HKHealthStore alloc]init];

NSCalendar *calendar = [NSCalendar currentCalendar];

// 设置时间支持单位

NSDateComponents *anchorComponents =

[calendar components:NSCalendarUnitDay | NSCalendarUnitMonth |

NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];

NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];

// 获取数据的截止时间 今天

NSDate *endDate = [NSDate date];

// 获取数据的起始时间 此处取从今日往前推100天的数据

NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-100*24*60*60];

// 数据类型

HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

// Your interval: sum by hour

NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];

intervalComponents.day = 1;

// Example predicate 用于获取设置时间段内的数据

NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:anchorDate intervalComponents:intervalComponents];

query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {

for (HKStatistics *sample in [result statistics]) {

//            NSLog(@"--------------%@ 至 %@ : %@", sample.startDate, sample.endDate, sample.sumQuantity);

NSDate *date = sample.endDate;

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

[formatter setDateFormat:@"yyyy-MM-dd"];

NSString *dateTime = [formatter stringFromDate:date];

double totalStep = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];

NSString *value = [NSString stringWithFormat:@"%f",totalStep];

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:

dateTime,@"dateTime",

value,@"value",nil];

[self.healthSteps addObject:dic];

//            NSLog(@"gaizaoDateStyle:%@  Dic = %@",self.healthSteps,dic);

}

self.healthCalories = self.healthSteps;

NSDictionary *healthSteps = [NSDictionary dictionaryWithObjectsAndKeys:

self.healthSteps,@"healthSteps",

self.healthCalories,@"healthCalories",nil];

NSLog(@"改造数据格式:%@",healthSteps);

};

[healthStore executeQuery:query];

}

由于苹果健康应用中,用户可以自己手动添加数据,所以为了获取数据的准确性,像微信运动会把这些数据过滤掉,那么如何过滤呢?

在获取的数据结果中,每一条结果都有数据来源属性,打印HKStatistics实例对象的sources属性就可以看到数据的所有来源(因为此处获取的是一天的数据,一天内的数据来源都会被打印出来),如果有手动添加的数据,可以在打印结果中看到com.apple.Health数据源,我们要过滤的就是它。

接下来就是用获取的数据总数把手动添加的数据减掉,结果就是我们想要获取的准确的数据。

*// 粘贴代码


double totalStep = [sample.sumQuantity doubleValueForUnit:[HKUnit countUnit]];

NSString *appleHealth = @"com.apple.Health";

double editStep  = 0.0;

for (HKSource *source in sample.sources) {

if ([source.bundleIdentifier isEqualToString:appleHealth]) {

// 获取用户自己添加的数据 并减去,防止用户手动刷数据

HKSource *healthSource = source;

editStep  = [[sample sumQuantityForSource:healthSource] doubleValueForUnit:[HKUnit countUnit]];

}

}

NSInteger step = (NSInteger)totalStep - (NSInteger)editStep;

NSString *value = [NSString stringWithFormat:@"%ld",step];

最后是获取距离的方法,和前边类似。

*// 代码


- (void)getHealthDistanceData{

HKHealthStore *healthStore = [[HKHealthStore alloc]init];

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *anchorComponents =

[calendar components:NSCalendarUnitDay | NSCalendarUnitMonth |

NSCalendarUnitYear | NSCalendarUnitWeekday fromDate:[NSDate date]];

NSDate *anchorDate = [calendar dateFromComponents:anchorComponents];

NSDate *endDate = [NSDate date];

NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-100*24*60*60];

HKQuantityType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];

// Your interval: sum by hour

NSDateComponents *intervalComponents = [[NSDateComponents alloc] init];

intervalComponents.day = 1;

// Example predicate

NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];

HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:type quantitySamplePredicate:predicate options:HKStatisticsOptionCumulativeSum anchorDate:anchorDate intervalComponents:intervalComponents];

query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *result, NSError *error) {

for (HKStatistics *sample in [result statistics]) {

//            NSLog(@"+++++++++++++++%@ 至 %@ : %@", sample.startDate, sample.endDate, sample.sumQuantity);

NSDate *date = sample.endDate;

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

[formatter setDateFormat:@"yyyy-MM-dd"];

NSString *dateTime = [formatter stringFromDate:date];

double totalDistance = [sample.sumQuantity doubleValueForUnit:[HKUnit meterUnit]];

NSString *appleHealth = @"com.apple.Health";

//            double floor = [sample.sumQuantity doubleValueForUnit:[HKUnit yardUnit]];

double editDistance  = 0.0;

for (HKSource *source in sample.sources) {

if ([source.bundleIdentifier isEqualToString:appleHealth]) {

// 获取用户自己添加的数据 并减去,防止用户手动刷数据

HKSource *healthSource = source;

editDistance = [[sample sumQuantityForSource:healthSource] doubleValueForUnit:[HKUnit meterUnit]];

}

}

double distance = totalDistance/1000 - editDistance/1000;

NSString *value = [NSString stringWithFormat:@"%f",distance];

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:

dateTime,@"dateTime",

value,@"value",nil];

[self.healthDistances addObject:dic];

}

NSLog(@"改造距离格式:%@",self.healthDistances);

};

[healthStore executeQuery:query];

}

在打包时候需要打开HealthKit使用的开关,如图:

屏幕快照 2016-08-25 下午5.15.31.png

此外打包证书也需要勾选苹果健康选项,由于开发者账号划归别的部门负责了,所以没法截图了。。。

踩的坑

之前做了一个健康运动的APP,要把苹果健康的数据和fitbit手环的数据比较处理后做展示,结果发现请求fitbit的数据产生的起止时间段内如果有没数据的,比如步数,卡路里,等,会都返回0,还比较人性化。但是苹果健康就没这么友好了,如果没有数据那就是没有,根本不会给你0,比如2015年1月1号到2015年1月20号的数据中1月10号这天的数据被删了,那返回的数据就只有19条,10号这天的不会返回0,直接就没有这条数据。再或者本身开始使用苹果健康记录数据的时间就是从2015年1月10号开始的,那1月10号之前的数据就都没有,不会返回0给你。

demo地址 GitHub给个Star噢!
喜欢就点个赞呗!
欢迎大家提出更好的改进意见和建议,一起进步!

上一篇下一篇

猜你喜欢

热点阅读