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、来源信息HKSource和HKSourceRevision、设备信息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);
}
}];