前言
开发中有需要读写苹果健康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
即可(HKQuantityType
是sampleType
的子类),代表要读取的类型 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
写入