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]; // 步数
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(@"文能设备,收集的睡眠记录保存成功");
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) {
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) {
nslog(@"文能设备,收集的睡眠记录删除成功 %@", @(deletedobjectcount));
nslog(@"文能设备,收集的睡眠记录删除失败 %@", error);