美文网首页iOS开发技能IT技术iOS开发技术
iOS开发-关于苹果健康数据的获取

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

作者: Mister志伟 | 来源:发表于2016-08-25 17:09 被阅读2501次
    • 关于iOS10的报错
    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噢!
    喜欢就点个赞呗!
    欢迎大家提出更好的改进意见和建议,一起进步!

    相关文章

      网友评论

      • 哎呦我去叫什么呢:求助,卡路里一直为0是为什么啊
        Mister志伟:@哎呦我去叫什么呢 https://zhidao.baidu.com/question/1450027652145305860.html
        哎呦我去叫什么呢:@Mister志伟 可以告诉我是什么公式吗
        Mister志伟:@哎呦我去叫什么呢 苹果数据如果没有写入的卡路里数据是没法获取卡路里的,我是按一个固定计算公式根据步数计算的。
      • feng_dev:如果要做 以当前日期 获取最近七天的步数,是不是只能每次 只获取一天的,然后获取 七次。。有没有单独获取 某一天的 api
        feng_dev:@Mister志伟 但是 这么查可能效率有点低,不过还感觉不出来。
        feng_dev:@Mister志伟 我的意思 是 ,获取七天的 每天 的具体数据,而不是 七天 步数的总和。我目前是查了七次,每次 一天。
        Mister志伟:获取数据是根据起止日期获取的,如果你要获取最近7天的,就传今天和7天前的日期就可以了。贴出的代码中有API。
      • 夜生物:大神,有没有方法获取每天最大步数?
        夜生物:@Mister志伟 可以获取到第一次记录运动步数的日期吗?
        Mister志伟:不好意思,看了下可以获取到步数,你下载demo,里边有代码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.startDate;
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM-dd"];
        NSString *dateTime = [formatter stringFromDate:date];

        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];

        NSLog(@"步数 :%@ ",value);
        }
        夜生物:@Mister志伟 如何获得第一次记录运动步数的日期呢?

      本文标题:iOS开发-关于苹果健康数据的获取

      本文链接:https://www.haomeiwen.com/subject/xrjasttx.html