HealthKit之理论知识


1、#import <HealthKit/HealthKit.h>


2、创建HKHealthStore对象,通过它可以读取和存储用户的health数据。不过在读取和存储之前,首先要通过HKHealthStore执行方法+ (BOOL)isHealthDataAvailable;判断用户设备是否支持HealthKit。


3、我们读取和存储用户的health数据,就必须明确知道要操作的数据类型。在HealthKit里,数据类型有HKObjectType、HKSampleType、HKActivitySummaryType、HKCategoryType、HKCharacteristicType、HKCorrelationType、HKQuantityType和HKWorkoutType。它们的结构关系如图所示。

通过在HKObjectType.h类里定义的方法,

+ (nullable HKQuantityType *)quantityTypeForIdentifier:(NSString *)identifier;

+ (nullable HKCategoryType *)categoryTypeForIdentifier:(NSString *)identifier;

+ (nullable HKCharacteristicType *)characteristicTypeForIdentifier:(NSString *)identifier;

+ (nullable HKCorrelationType *)correlationTypeForIdentifier:(NSString *)identifier;

+ (HKWorkoutType *)workoutType;

+ (HKActivitySummaryType *)activitySummaryType NS_AVAILABLE_IOS(9_3);

可以去创建对应的数据类型。方法参数Identifier,是具体数据Id,在HKTypeIdentitytifiers.h类里定义。它们命名方式都是以所属类型名开始。比如:

    HKObjectType *type1 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; // 步数

    HKObjectType *type2 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; // 步行+跑步距离

    HKObjectType *type3 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed];     // 已爬楼层

    HKObjectType *tyep4 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned]; // 活动能量

    HKObjectType *type5 = [HKObjectType activitySummaryType];// 健身记录


    HKObjectType *type6 = [HKObjectType correlationTypeForIdentifier:HKCorrelationTypeIdentifierFood];

    

    HKObjectType *type8 = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex]; // 性别

    HKObjectType *type9 = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBloodType];//血型

    HKObjectType *type10 = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];//出生日期

    HKObjectType *type11 = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierFitzpatrickSkinType];//日光反应型皮肤类型

    

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];// 睡眠分析

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierAppleStandHour]; // 站立小时

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierCervicalMucusQuality]; // 宫颈粘液质量

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierOvulationTestResult]; // 排卵测试结果

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierMenstrualFlow]; // 月经

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierIntermenstrualBleeding];// 点滴出血

    HKObjectType *type12 = [HKObjectType categoryTypeForIdentifier:HKCategoryTypeIdentifierSexualActivity]; // 性行为


4、在读取和存储用户的health数据之前,我们需要请求授权,读取和存储哪些类型的Health数据。通过HKHealthStore对象执行- (void)requestAuthorizationToShareTypes:(nullable NSSet<HKSampleType *> *)typesToShare

                               readTypes:(nullable NSSet<HKObjectType *> *)typesToRead

                              completion:(void (^)(BOOL success, NSError * __nullable error))completion;参数shareTypes就是需要授权的存储数据类型,参数readTypes就是需要授权的读取数据类型,completion就是授权完成回调。授权过程是在子线程异步执行。代码参考:

HKObjectType *type1 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]; // 步数

    HKObjectType *type2 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]; // 步行+跑步距离

    HKObjectType *type3 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierFlightsClimbed];     // 已爬楼层

    HKObjectType *tyep4 = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned]; // 活动能量

    NSSet *rset = [NSSet setWithObjects:type1, type2, type3, tyep4, nil]; // 读集合

    

    

    HKSampleType *wType1 = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    HKCategoryType *wType2 = [HKCategoryType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];

    NSSet *wset = [NSSet setWithObjects:wType1, wType2, nil]; // 写集合

    

    __weak typeof (&*self) weakSelf = self;

    [_healthStore requestAuthorizationToShareTypes:wset readTypes:rset completion:^(BOOL success, NSError * _Nullable error) {

        if (success)

        {

            // 去读取和存储用户的health数据

        } else

        {

            NSLog(@"healthkit不允许读写");

        }

    }];


5、读取用户health数据,HKHealthStore提供了

- (nullable NSDate *)dateOfBirthWithError:(NSError **)error;

- (nullable HKBiologicalSexObject *)biologicalSexWithError:(NSError **)error;

- (nullable HKBloodTypeObject *)bloodTypeWithError:(NSError **)error;

- (nullable HKFitzpatrickSkinTypeObject *)fitzpatrickSkinTypeWithError:(NSError **)error;

可以直接读用户的出生日期、性别、血型和日光反应型皮肤类型。

而需要读取更多的数据,就得创建查询条件去查询数据,查询就得用到HKQuery,它的子类有:

HKSampleQuery(HKSampleType类型数据查询,简称样本查询),HKActivitySummaryQuery(HKActivitySummaryType数据类型查询,即健身记录查询),

HKCorrelationQuery(HKCorrelationType数据类型查询),

HKAnchoredObjectQuery(锚点查询,即分页查询),

HKObserverQuery(观察查询,即查询的数据发生变化时能监听到),

HKStatisticsQuery(统计查询,比如统计今天步数),

HKStatisticsCollectionQuery(从某一时间开始,以一定的时间间隔按条件查询,比如统计今天活动记录中每一小时的步数,即6点多少步,8店多少步,15点多少步),

HKSourceQuery(数据来源查询,比如查询来源小米手环的所有数据)。

代码参考:

// 查询今天步数

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

    NSDateComponents *dateCom = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:[NSDate date]];

    

    NSDate *endDate = [calendar dateFromComponents:dateCom];

    

    [dateCom setHour:0];

    [dateCom setMinute:0];

    [dateCom setSecond:0];

    

    NSDate *startDate = [calendar dateFromComponents:dateCom];

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


HKQuantityType *sampleType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    

    //NSSortDescriptors用来告诉healthStore怎么样将结果排序。

    NSSortDescriptor *start = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:NO];

    NSSortDescriptor *end   = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierEndDate ascending:NO];


__weak typeof (&*_healthStore)weakHealthStore = _healthStore;

    

    HKSampleQuery *q1 = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[start,end] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {

        double sum = 0;

        double sumTime = 0;

        NSLog(@"步数结果=%@", results);

        for (HKQuantitySample *res in results)

        {

            

            sum += [res.quantity doubleValueForUnit:[HKUnit countUnit]];

            

            NSTimeZone *zone = [NSTimeZone systemTimeZone];

            NSInteger interval = [zone secondsFromGMTForDate:res.endDate];

            

            NSDate *startDate = [res.startDate dateByAddingTimeInterval:interval];

            NSDate *endDate   = [res.endDate dateByAddingTimeInterval:interval];


            sumTime += [endDate timeIntervalSinceDate:startDate];

        }

        int h = sumTime / 3600;

        int m = ((long)sumTime % 3600)/60;

        NSLog(@"运动时长:%@小时%@分", @(h), @(m));

        NSLog(@"运动步数:%@步", @(sum));

        if(error) NSLog(@"1error==%@", error);

        [weakHealthStore stopQuery:query];

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

                //查询是在多线程中进行的,如果要对UI进行刷新,要回到主线程中

            }];;

    }];

//执行查询

    [_healthStore executeQuery:q1];

查询完成时,有一处还需要我们注意,就是单位转换。单位类HKUnit,哪怎么知道查询得到的数据是什么单位呢,在HKTypeIdentifiers.h类里定义数据Id后面都有标明,

比如:

HK_EXTERN NSString * const HKQuantityTypeIdentifierStepCount NS_AVAILABLE_IOS(8_0);                 // Scalar(Count),               Cumulative

步数的单位是Scalar(Count),我们就到HKUnit.h类搜索Scalar,找到HKUnit扩展@interface HKUnit (Scalar);而(Count)表明需要执行此扩展的+ (instancetype)countUnit; 得到所需的单位类。

当然我也可以为数据类自定义单位类。

通过HKHealthStore对象的方法来做到

- (void)preferredUnitsForQuantityTypes:(NSSet<HKQuantityType *> *)quantityTypes completion:(void(^)(NSDictionary<HKQuantityType*, HKUnit *> *preferredUnits, NSError * __nullable error))completion NS_AVAILABLE_IOS(8_2);


6、存储和删除health数据,通过HKHealthStore对象的方法来做到

存储方法

- (void)saveObject:(HKObject *)object withCompletion:(void(^)(BOOL success, NSError * __nullable error))completion;

- (void)saveObjects:(NSArray<HKObject *> *)objects withCompletion:(void(^)(BOOL success, NSError * __nullable error))completion;


删除方法

- (void)deleteObject:(HKObject *)object withCompletion:(void(^)(BOOL success, NSError * __nullable error))completion;

- (void)deleteObjects:(NSArray<HKObject *> *)objects withCompletion:(void(^)(BOOL success, NSError * __nullable error))completion NS_AVAILABLE_IOS(9_0);

- (void)deleteObjectsOfType:(HKObjectType *)objectType predicate:(NSPredicate *)predicate withCompletion:(void(^)(BOOL success, NSUInteger deletedObjectCount, NSError * __nullable error))completion NS_AVAILABLE_IOS(9_0);


这里主要介绍下参数object,是HKObject数据对象,其包括UUID、来源信息HKSourceHKSourceRevision、设备信息HKDevice和元数据HKMetadata。

HKSource,包括来源名称和唯一标识符,系统8.0以下使用。

HKSourceRevision,包括来源名称、唯一标识符和版本号,系统8.0以上使用。

HKDevice,包括设备的名称、制造商、型号、硬件版本和软件版本等。

HKMetadata,可以设置设备序列号、身体温度传感器的位置、心率传感器的位置、数字签名、是否是室内室外锻炼等。

HKObject有四个子类HKSample、HKWorkout、HKCorrelation和HKCategorySample。


代码参考:

// 睡眠分析

    HKCategoryType *categoryType = [HKCategoryType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];

    HKDevice *device = [[HKDevice alloc] initWithName:@"文能设备" manufacturer:@"中国制造商" model:@"智能机" hardwareVersion:@"1.0.0" firmwareVersion:@"1.0.0" softwareVersion:@"1.0.0" localIdentifier:@"lizaochengwen" UDIDeviceIdentifier:@"wennengshebei"];

    HKCategorySample *testObject = [HKCategorySample categorySampleWithType:categoryType value:0.25 startDate:[NSDate dateWithTimeIntervalSinceNow:-(24 * 3600)] endDate:[NSDate date] device:device metadata:nil];

    [_healthStore saveObject:testObject withCompletion:^(BOOL success, NSError * _Nullable error) {

        if (success) {

            NSLog(@"文能设备,收集的睡眠记录保存成功");

        } else

        {

             NSLog(@"文能设备,收集的睡眠记录保存失败 %@", error);

        }

    }];

    

    NSMutableArray *list= [[NSMutableArray alloc] init];

    for (float i = 1; i < 100; i++) {

        HKCategorySample *testObject = [HKCategorySample categorySampleWithType:categoryType value:i/100.0 startDate:[NSDate dateWithTimeIntervalSinceNow:-(24 * 3600/i)] endDate:[NSDate date] device:device metadata:nil];

        [list addObject:testObject];

    }

    [_healthStore saveObjects:list withCompletion:^(BOOL success, NSError * _Nullable error) {

        if (success) {

            NSLog(@"文能设备,收集的睡眠记录保存成功");

        } else

        {

            NSLog(@"文能设备,收集的睡眠记录保存失败 %@", error);

        }

    }];

    

    NSSet *dSet= [[NSSet alloc] initWithObjects:@"文能设备", nil];

    NSPredicate *catePredicate = [HKQuery predicateForObjectsWithDeviceProperty:HKDevicePropertyKeyName allowedValues:dSet];

    [_healthStore deleteObjectsOfType:categoryType predicate:catePredicate withCompletion:^(BOOL success, NSUInteger deletedObjectCount, NSError * _Nullable error) {

        if (success) {

            NSLog(@"文能设备,收集的睡眠记录删除成功 %@", @(deletedObjectCount));

        } else

        {

            NSLog(@"文能设备,收集的睡眠记录删除失败 %@", error);

        }

    }];

上一篇:React Native热更新方案


下一篇:使用MAC OS原生NTFS功能