Page 1 of 1

HealthKit接入总结

Posted: 2024年 Oct 10日 13:58
by wangyin

前言

开发中有需要读写苹果健康app数据的场景,需要使用HealthKit,这里对相关的用法做一个总结分享

1. 准备工作

1.1 判断是否支持使用HealthKit

可以使用下面这个方法

Code: Select all

[HKHealthStore isHealthDataAvailable];

1.2 数据的类型

HealthKit中有几种类型的数据,后续对于权限的申请和读写操作都需要明确数据的内心

  • quantityType 身高体重等数据,大部分数据都可读写,部分数据只读
  • categoryType 刷牙事件,心率事件等,可读写
  • CharacteristicType 性别,血型等数据,只读
  • CorrelationType 关联类型,上面的类型都只用一个值就可以写入,一个值表示不了的就需要用到这个关联类型来组织一组值,目前只有两种血压和食物组成

2. 权限获取

在获取权限前需要添加下面的权限获取说明

Code: Select all

<key>Privacy - Health Update Usage Description</key>
<string>health</string>

<key>Privacy - Health Share Usage Description</key>
<string>health</string>

2.1 权限申请

Code: Select all

- (void)requestAuthorizationToShareTypes:(nullable NSSet<HKSampleType *> *)typesToShare
                                readTypes:(nullable NSSet<HKObjectType *> *)typesToRead
                                completion:(void (^)(BOOL success, NSError * _Nullable error))completion

可以通过这种方式来获取权限,中typesToRead是要获取的读权限的数据类型,typesToShare是要获取的写权限的数据类型

2.2 权限查询

Code: Select all

- (HKAuthorizationStatus)authorizationStatusForType:(HKObjectType *)type;
// [self.healthStore authorizationStatusForType:objectType]

可以通过该方法查询是否有写入权限,对于读取权限苹果官方做了限制无法查询,没权限时进行读取会读到空数据

2.2 类型构造

Code: Select all

+ (nullable HKQuantityType *)quantityTypeForIdentifier:(HKQuantityTypeIdentifier)identifier;
+ (nullable HKCategoryType *)categoryTypeForIdentifier:(HKCategoryTypeIdentifier)identifier;
+ (nullable HKCharacteristicType *)characteristicTypeForIdentifier:(HKCharacteristicTypeIdentifier)identifier;

通过这些方法可以构造出需要的类型,用来获取权限,其中的identifier可以去HKTypeIdentifiers.h文件中查找

3. 数据读取

3.1 quantityType类别读取

读取quantityType类型需要先构造一个HKSampleQuery

Code: Select all

- (instancetype)initWithSampleType:(HKSampleType *)sampleType
                         predicate:(nullable NSPredicate *)predicate
                             limit:(NSUInteger)limit
                   sortDescriptors:(nullable NSArray<NSSortDescriptor *> *)sortDescriptors
                    resultsHandler:(void(^)(HKSampleQuery *query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error))resultsHandler;
  • 其中sampleType就传一个HKQuantityType即可(HKQuantityTypesampleType的子类),代表要读取的类型
  • predicate,查询的时间范围,可以通过下面的方法构造

    Code: Select all

    + (NSPredicate *)predicateForSamplesWithStartDate:(nullable NSDate *)startDate 
                                            endDate:(nullable NSDate *)endDate 
                                            options:(HKQueryOptions)options;
  • limit限制返回数量
  • sortDescriptors数据排序标识
  • resultsHandler 完成查询的回调,results包含查询到的数据

构造完成后使用HKHealthStore实例执行即可

Code: Select all

[self.healthStore executeQuery:sampleQuery];

3.1.1 数据解析

查询到的resluts中的数据是HKQuantitySample类型,数据就在quantity属性中,要获取到数据可以通过HKQuantity类中的方法

Code: Select all

- (double)doubleValueForUnit:(HKUnit *)unit;

其中unit是数据的单位,不通的数据对应不同单位,单位传错会crash,单位也可以在HKTypeIdentifiers.h文件中查看

Code: Select all

// HKUnit 构造
+ (instancetype)unitFromString:(NSString *)string;

3.2 categoryType类别读取

categoryType读取和quantityType类型,只在传入要读取的类型时传入一个HKCategoryTyp即可
对于读取到的数据是HKCategorySample类型,不需要单位,直接取里面的value即可

3.2.1 数据解析

对于读取到的数据含义参考不同类型的官方文档,比如HKCategoryTypeIdentifierHandwashingEvent类型,它的值为HKCategoryValueNotApplicable也就是0,代表一次洗手时间,通过获取HKCategorySample中的起始和终止时间来表示这次洗手的时间,同样写入时也只能把value设置为0,用起止时间来表示持续时间

3.3 CharacteristicType类别读取

CharacteristicType类别读取和前两种都不同,对于不同的类型有不同api

Code: Select all

NSError * error;
// HKCharacteristicTypeIdentifierBiologicalSex
HKBiologicalSexObject * res = [self.healthStore biologicalSexWithError:&error];
// HKCharacteristicTypeIdentifierBloodType
HKBloodTypeObject * res = [self.healthStore bloodTypeWithError:&error];
// HKCharacteristicTypeIdentifierDateOfBirth
NSDateComponents * dateComponents = [self.healthStore dateOfBirthComponentsWithError:&error];
// HKCharacteristicTypeIdentifierFitzpatrickSkinType
HKFitzpatrickSkinTypeObject * res = [self.healthStore fitzpatrickSkinTypeWithError:&error];
// HKCharacteristicTypeIdentifierWheelchairUse
HKWheelchairUseObject * res = [self.healthStore wheelchairUseWithError:&error];

4. 数据写入

4.1 quantityType类别写入

先用要写入的数据构造一个HKQuantity,单位和读取一样也要传入匹配的

Code: Select all

+ (instancetype)quantityWithUnit:(HKUnit *)unit doubleValue:(double)value;
// [HKQuantity quantityWithUnit:hkUnit doubleValue:value];

然后构造一个HKQuantitySample准备写入

Code: Select all

+ (instancetype)quantitySampleWithType:(HKQuantityType *)quantityType
                              quantity:(HKQuantity *)quantity
                             startDate:(NSDate *)startDate
                               endDate:(NSDate *)endDate;
// HKQuantitySample *quantitySample = [HKQuantitySample quantitySampleWithType:quantityType quantity:quantity startDate:startDate endDate:endDate];

最后通过healthStore写入,注意写入时要注意线程安全问题

Code: Select all

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

4.2 categoryType类别写入

直接构造一个HKCategorySample准备进行写入
对于需要写入的value参考不同类型的官方文档,比如HKCategoryTypeIdentifierHandwashingEvent类型,它的值为HKCategoryValueNotApplicable也就是0,写入时只能把value设置为0,用起止时间来表示持续时间

Code: Select all

+ (instancetype)categorySampleWithType:(HKCategoryType *)type
                                 value:(NSInteger)value
                             startDate:(NSDate *)startDate
                               endDate:(NSDate *)endDate;
// HKCategorySample *sample = [HKCategorySample categorySampleWithType:Type value:aValue startDate:startDate endDate:endDate];

然后通过healthStore写入,注意写入时要注意线程安全问题

Code: Select all

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

4.3 CorrelationType类别写入

一些特殊数据单独用上面的方法写入不会展示在健康app中,比如血压,虽然收缩压和舒张压都是quantityType,但是单独用上面的方式写入是不行的,因为血压这两个数据是成对出现的
下面以血压为例看看怎么写入

  • 首先创建收缩压和舒张压的HKQuantitySample
  • 然后将两个数据放到一个set

    Code: Select all

    NSMutableSet *objects = [[NSMutableSet alloc] init];
    [objects addObject:systolicSample];
    [objects addObject:diastolicSample];
  • 用包含两个数据的set创建HKCorrelation

    Code: Select all

    + (instancetype)correlationWithType:(HKCorrelationType *)correlationType
                              startDate:(NSDate *)startDate
                                endDate:(NSDate *)endDate
                                objects:(NSSet<HKSample *> *)objects;
    // HKCorrelation *bloodPressure = [HKCorrelation correlationWithType:bloodPressureType startDate:startDate endDate:endDate objects:objects];
  • 最后和上面一样通过healthStore写入