天天看点

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]; // 步数

    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);