iOS之healthKit



参考:http://www.cocoachina.com/ios/20140915/9624.html

原理:
HealthKit中所有的对象都是HKObject的子类。大部分  HKObject 对象子类都是不可变的。每个对象都有下列属性:
 
1.  UUID。每个对象的唯一标示符。
 
2.  Source。数据的来源。来源可以是直接把数据存进HealthKit的设备,或者是应用。当一个对象保存进HealthKit中时,HealthKit会自动设置其来源。只有从HealthKit中获取的数据source属性才可用。
 
3.  Metadata。一个包含关于该对象额外信息的字典。元数据包含预定义和自定义的键。预定义的键用来帮助在应用间共享数据。自定义的键用来扩展HealthKit对象类型,为对象添加针对应用的数据。
 
HealthKit对象主要分为2类:特征和样本。特征对象代表一些基本不变的数据。包括用户的生日、血型和生理性别。你的应用不能保存特征数据。用户必须通过健康应用来输入或者修改这些数据。
 
样本对象代表某个特定时间的数据。所有的样本对象都是 HKSample的子类。它们都有下列属性:
 
1.  Type。样本类型。例如,这可能包括一个睡眠分析样本、一个身高样本或者一个计步样本。
 
2.  Start date。样本的开始时间。
 
3.  End date。样本的结束时间。如果样本代表时间中的某一刻,结束时间和开始时间相同。如果样本代表一段时间内收集的数据,结束时间应该晚于开始时间。
 
样本可以细分为四个样本类型。
1.  类别样本。这种样本代表一些可以被分为有限种类的数据。在iOS8.0中,只有一种类别样本,睡眠分析。更多信息,参见 HKCategorySample Class Reference
 
2.  数量样本。这种样本代表一些可以存储为数值的数据。数量样本是HealthKit中最常见的数据类型。这些包括用户的身高和体重,还有一些其他数据,例如行走的步数,用户的体温和脉搏率。更多信息,参见 HKQuantitySample Class Reference
 
3.  Correlation。这种样本代表复合数据,包含一个或多个样本。在iOS8.0中,HealthKit使用correlation来代表食物和血压。在创建书屋或者血压数据时,你应该使用correlation。更多信息,参见  HKCorrelation Class Reference
 
4.  Workout。Workout代表某些物理活动,像跑步、游泳,甚至游戏。Workout通常有类型、时长、距离、和消耗能量这些属性。你还可以为一个workout关联许多详细的样本。不像correlation,这些样本是不包含在workout里的。但是,它们可以通过workout获取到。更多信息,参见   HKWorkout Class Reference
 
设置HealthKit
在你开始使用HealthKit之前,必须要执行下列步骤:
 
1. 在Xcode中打开HealthKit功能。
 
2. 调用  isHealthDataAvailable 方法来查看HealthKit在该设备上是否可用。HealthKit在iPad上不可用。
 
3. 为你的应用实例化一个  HKHealthStore 对象。每个应用只需要一个HealthKit存储实例。这个存储实例就是你和HealthKit数据库交互的主要接口。
 
4. 使用   requestAuthorizationToShareTypes:readTypes:completion:方法来请求获取HealthKit数据的权限。对每种类型的数据,你都必须请求许可来共享和读取。
 
如果用户允许分享某种类型的数据,那么你可以创建这种类型的新样本,并保存在HealthKit中。你应该使用  authorizationStatusForType:来检查是否允许分享这种类型的数据。
 
如果用户允许读取某种类型的数据,那么你就可以从HealthKit中读取这些数据。不幸的是,即使知道用户拒绝读取某种类型的数据,也可能会显示出潜在的健康问题。因此,你的应用无法确定用户是否允许读取数据。如果你没有得到读取某种数据的许可,那简单来看就好像是HealthKit中没有这种类型的数据。
 
关于设置HealthKit的更多内容,参见 HKHealthStore Class Reference
 
有一个展示了如何设置和使用HealthKit的例子,参见 Fit: Store and Retrieve HealthKit Data
 
向HealthKit 存储中添加样本
你的应用可以创建新的样本,并添加到HealthKit存储中。对所有样本类型来说,大致的过程都是类似的,但是又有一些不同。
 
1.  在  HealthKit Constants Reference 中找到正确的类型标识符。
 
2.  使用类型标识符创建一个匹配的  HKObjectType 子类。有一些便捷的方法,参见    HKObjectType Class Reference
 
3.  使用对象类型,创建一个匹配的  HKSample 子类。
 
4.  使用  saveObject:withCompletion:  方法将对象保存到HealthKit 存储中。
 
每个  HKSample 子类都有自己的便捷方法来实例化样本对象。流程如下图所示。
对于数量样本,你必须创建一个  HKQuantity  类的实例。数量的单位必须和类型标识符文档中描述的可用单位相关。例如, HKQuantityTypeIdentifierHeight 文档中说明它使用长度单位,因此,你的数量必须使用厘米、米、英尺、英寸或者其他长度单位。更多信息,参见  HKQuantitySample Class Reference
对于类别样本,它的值必须和类型标识符文档中描述的枚举值相关。例如,  HKCategoryTypeIdentifierSleepAnalysis 文档中说明它使用  HKCategoryValueSleepAnalysis 枚举值。因此你在创建样本时必须从这个枚举中传递一个值。更多信息,参见  HKCategorySample Class Reference
对于correlation,你必须先创建correlation包含的所有样本。correlation的类型标识符描述了它可以包含的类型和对象的数量。不要把被包含的对象存进HealthKit。它们是以correlation的一部分存储的。更多信息,参见 HKCorrelation Class Reference
workout和其他类型的样本有些不同。首先,创建  HKWorkoutType 实例并不需要指定类型标识符。所有的workout都是用同样的类型标识符。第二,对于每个workout你都需要提供一个  HKWorkoutActivityType 值。这个值定义了workout中执行的活动的类型。最后,当workout保存到HealthKit后,你可以给workout关联额外的样本。这些样本提供了workout的详细信息。更多信息,参见 HKWorkout Class Reference
 
读取HealthKit数据
HealthKit提供了许多方法来读取数据。
 
1.  直接方法调用。HealthKit提供了直接读取特征数据的方法。这些方法只能用于读取特征数据。更多信息,参见  HKHealthStore Class Reference
 
2.  样本查询。这是使用最多的查询。使用样本查询来读取任何类型的样本数据。当你想要对结果进行排序或者限制返回的样本总数时,样本查询就特别有用。更多信息,参见  HKSampleQuery Class Reference
 
3.  观察者查询。这是一个长时间运行的查询,它会检测HealthKit存储,并在匹配到的样本发生变化时通知你。如果当存储发生变化时你想得到通知,就使用观察者查询。你可以让这些查询在后台执行。更多信息,参见 HKObserverQuery Class Reference
 
4.  锚定对象查询。用这种查询来搜索添加进存储的项。当锚定查询第一次执行时,会返回存储中所有匹配的样本。在接下来的执行中,只会返回上一次执行之后添加的项目。通常,锚定对象查询会和观察者查询一起使用。观察者查询告诉你某些项目发生了变化,而锚定对象查询来决定有哪些(如果有的话)项目被添加进了存储。更多信息,参见  HKAnchoredObjectQuery Class Reference
 
5.  统计查询。使用这种查询来在一系列匹配的样本中执行统计运算。你可以使用统计查询来计算样本的总和、最小值、最大值或平均值。更多信息,参见  HKStatisticsQuery Class Reference
 
6.  统计集合查询。使用这种查询来在一系列长度固定的时间间隔中执行多次统计查询。通常使用这种查询来生成图表。查询提供了一些简单的方法来计算某些值,例如,每天消耗的总热量或者每5分钟行走的步数。统计集合查询是长时间运行的。查询可以返回当前的统计集合,也可以监测HealthKit存储,并对更新做出响应。更多信息,参见  HKStatisticsCollectionQuery Class Reference
 
7.  Correlation查询。使用这种查询来在correlation查找数据。这种查询可以为correlation中每个样本类型包含独立的谓词。如果你只是想匹配correlation类型,那么请使用样本查询。更多信息,参见  HKCorrelation Class Reference
 
8.  来源查询。使用这种查询来查找HealthKit存储中的匹配数据的来源(应用和设备)。来源查询会列出储存的特定样本类型的所有来源。更多信息,参见 HKSourceQuery Class Reference
 
单位换算
HealthKit使用  HKUnit 和  HKQuantity 类来支持单位。 HKUnit 提供了单一单位的表示。它支持大部分的公制和英制单位,当然还包括基本单位和符合单位。基本单位代表单一的度量,例如米、磅或者秒。复合单位使用数学运算连接一个或多个基本单位,例如m/s或者lb/ft2。
 
HKUnit 提供了便捷方法来创建HealthKit支持的所有基本单位。它还提供了构建复合单位需要的数学运算。最后,你还可以通过直接使用恰当的格式化的单位字符串来创建复合单位。
 
更多信息,参见 HKUnit Class Reference
 
HKQuantity 类存储了给定单位的值。之后你可以用任何兼容的单位来取值。这样,你的应用就可以很轻松的完成单位换算。
 
更多信息,参见  HKQuantity Class Reference
 
在某些场合,你可以使用格式化器来本地化数量。iOS8提供了提供了新的格式化器来处理长度(NSLengthFormatter)、质量(NSMassFormatter)和能量(NSEnergyFormatter)。对于其他的数量,你需要自己来换算单位和本地化数据。
 
多线程
HealthKit存储是线程安全的,大部分HealthKit对象是不可变的。通常来说,你可以在多线程环境中安全地使用HealthKit。
 
注意:
所有HealthKit API的完成回调都在一个私有的后台队列中执行。所以在你更新用户界面或者修改一些只能在主线程中处理的资源之前,你应该把这些数据传回主线程。
 
关于多线程和并发编程的更多信息,参见  Concurrency Programming Guide
 
添加数字签名
HealthKit中的数字签名元数据对象提供了由可信设备生成的样本记录的数据完整性。元数据项存储了一份数字签名过的样本记录副本。签名是由设备生成的(由于设备存储了私有签名密钥,所以设备应该是防篡改的)。这样数据的使用者就可以通过设备的公钥来检查签名,进而验证记录数据是否被修改过。由于每条记录都是独立签名的,所以每条记录总共能存储大约1K bytes的签名。因此,这种元数据签名项是给那些样本率最多在每天几次的记录使用的。更高的样本率需要对样本组进行签名,这已经超出了本文档的范围。
 
通常,私钥是在制造时置入防篡改的测量设备中的。(私钥重置或证书更新的政策或机制不在本文档的范围内)相关的公钥会由设备制造商公布,例如在它们的网站上。设备应该将每个样本的样本数据和签名传给iOS应用,这两者都会储存在HealthKit数据库中。注意,数字签名的公私钥是用来提供数据完整性检查的,不是用来加密的。数据记录中的实际值都是明文。
 
数字签名的格式是IETF RFC 5652中的CMS(Cryptographic Message Syntax)。签名使用ASN.1的DER(Distinguished Encoding Rules)编码。使用的信息摘要应该是SHA256,签名加密应该是FIPS PUB 186-4 Digital Signature Standard Elliptic Curve P-256。这样长度和效率都有保证。另外,整个签名都应该使用bsae64编码,这样就能存储在HealthKit NSString 元数据对象中。
 
签名应该是ASN.1 Signed-data Content Type:
SignedData ::= SEQUENCE {   version CMSVersion,   digestAlgorithms DigestAlgorithmIdentifiers,   encapContentInfo EncasulatedContentInfo,   signerInfos SignerInfo }
 
SignerInfo Type是:
SignerInfo ::= SEQUENCE {   version CMSVersion,   sid SignerIdentifier,   digestAlgorithm DigestAlgorithmIdentifier,   signatureAlgorithem SignatureAlgorithmIdentifier,   signatureSignatureValue }
 
上面就是摘要和签名的算法。可选项目都没有包含在其中。SignerIdentifier用来取得适当的公钥,来对签名进行验证。 
 
EncapsulatedContentInfo是从由设备生成的样本记录中的相关项目的副本。副本应该用ASN.1 DER编码,并应至少包含一个样本时间戳和采样值。将记录数据拷贝进签名内,是为了有一个稳定的定义良好的二进制编码(ASN.1 DER),这对于生成一个可验证的签名来说是必要的




导入头文件:

#import <UIKit/UIKit.h>

#import <HealthKit/HealthKit.h>///健康的头文件


@interface ViewController : UIViewController

@property (nonatomic,strong)HKHealthStore *healthStore;


@end


//===========获取步数

#import "ViewController.h"


@interface ViewController ()

@property (strong,nonatomicUILabel *StepsLable;


@end


@implementation ViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    UILabel *StepsLable=[[UILabelalloc]initWithFrame:CGRectMake(30, 100, 200, 50)];

    self.StepsLable=StepsLable;

    [self.viewaddSubview:StepsLable];

    

//    self.StepsLable.layer.cornerRadius = self.StepsLable.frame.size.width/2;

    self.StepsLable.layer.borderColor = [UIColor redColor].CGColor;

    self.StepsLable.layer.borderWidth = 5;

    self.StepsLable.layer.masksToBounds = YES;

    

    NSTimer *timer=[NSTimerscheduledTimerWithTimeInterval:1target:selfselector:@selector(timerrepeat)userInfo:nilrepeats:YES];

    [[NSRunLoopcurrentRunLoop]addTimer:timerforMode:NSRunLoopCommonModes];

   

}

-(void)timerrepeat{

    NSLog(@"定时器执行");

 [selfgetAuthority];

}

#pragma mark 获取权限

- (void)getAuthority

{

    //查看healthKit在设备上是否可用,iPad上不支持HealthKit

    if (![HKHealthStoreisHealthDataAvailable]) {

        self.StepsLable.text = @"该设备不支持HealthKit";

    }

    

    //创建healthStore对象

    self.healthStore = [[HKHealthStorealloc]init];

    //设置需要获取的权限类型这里仅设置了步数

    HKObjectType *stepType = [HKObjectTypequantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    NSSet *healthSet = [NSSetsetWithObjects:stepType,nil];//权限集合

    

    //从健康应用中获取权限

    [self.healthStorerequestAuthorizationToShareTypes:nilreadTypes:healthSet completion:^(BOOL success,NSError * _Nullable error) {

        if (success) {

           //获取步数后我们调用获取步数的方法

            [selfreadStepCount];

        }

        else

        {

            self.StepsLable.text =@"获取步数权限失败";

        }

    }];

}

#pragma mark 读取步数查询数据

- (void)readStepCount

{

    //查询采样信息

    HKSampleType *sampleType = [HKQuantityTypequantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

    //NSSortDescriptor来告诉healthStore怎么样将结果排序

    NSSortDescriptor *start = [NSSortDescriptorsortDescriptorWithKey:HKSampleSortIdentifierStartDateascending:NO];

    NSSortDescriptor *end = [NSSortDescriptorsortDescriptorWithKey:HKSampleSortIdentifierEndDateascending:NO];

    //获取当前时间

    NSDate *now = [NSDatedate];

    NSCalendar *calender = [NSCalendarcurrentCalendar];

    NSUInteger unitFlags =NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;

    NSDateComponents *dateComponent = [calendercomponents:unitFlags fromDate:now];

    int hour = (int)[dateComponenthour];

    int minute = (int)[dateComponentminute];

    int second = (int)[dateComponentsecond];

    NSDate *nowDay = [NSDatedateWithTimeIntervalSinceNow:  - (hour*3600 + minute * 60 + second) ];

    //时间结果与想象中不同是因为它显示的是0

//    NSLog(@"今天%@",nowDay);

    NSDate *nextDay = [NSDatedateWithTimeIntervalSinceNow:  - (hour*3600 + minute * 60 + second)  + 86400];

//    NSLog(@"明天%@",nextDay);

    

    NSPredicate *predicate = [HKQuerypredicateForSamplesWithStartDate:nowDayendDate:nextDay options:(HKQueryOptionNone)];//谓词过滤,这里过滤的是今天的数据

    

    /*查询的基类是HKQuery,这是一个抽象类,能够实现每一种查询目标,这里我们需要查询的步数是一个HKSample类所以对应的查询类是HKSampleQuery(继承与HKQuery)。下面的limit参数传1表示查询最近一条数据,查询多条数据只要设置limit的参数值(无论什么值)就可以了*/

    

    HKSampleQuery *sampleQuery = [[HKSampleQueryalloc]initWithSampleType:sampleTypepredicate:predicate limit:0 sortDescriptors:@[start,end]resultsHandler:^(HKSampleQuery *_Nonnull query,NSArray<__kindofHKSample *> * _Nullable results,NSError * _Nullable error) {

        //设置一个int型变量来作为步数统计

        int allStepCount = 0;

        for (int i = 0; i < results.count; i ++) {

            //把结果转换为字符串类型

            HKQuantitySample *result = results[i];

            HKQuantity *quantity = result.quantity;

            NSMutableString *stepCount = (NSMutableString *)quantity;

            NSString *stepStr =[NSString stringWithFormat:@"%@",stepCount];

            //获取51 count此类字符串前面的数字

            NSString *str = [stepStrcomponentsSeparatedByString:@" "][0];

            int stepNum = [strintValue];

//            NSLog(@"%d",stepNum);

           //把一天中所有时间段中的步数加到一起

            allStepCount = allStepCount + stepNum;

        }

        NSLog(@"--总步数-----%d",allStepCount);

        //查询要放在多线程中进行,如果要对UI进行刷新,要回到主线程

        [[NSOperationQueuemainQueue]addOperationWithBlock:^{

            self.StepsLable.text = [NSStringstringWithFormat:@"%d",allStepCount];

        }];

    }];

    

    //执行查询

    [self.healthStoreexecuteQuery:sampleQuery];

}

- (void)didReceiveMemoryWarning {

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}



@end



===========方法二++++++

-(void)testone{

//创建healthStore实例对象

self.healthStore = [[HKHealthStore alloc] init];


//设置需要获取的权限这里仅设置了步数

HKObjectType *stepCount = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];


NSSet *healthSet = [NSSet setWithObjects:stepCount, nil];


//从健康应用中获取权限

[self.healthStore requestAuthorizationToShareTypes:nil readTypes:healthSet completion:^(BOOL success, NSError * _Nullable error) {

    if (success)

    {

        NSDateFormatter *formatter = [[NSDateFormatter alloc ]init];

        [formatter setDateFormat:@"yyyy-MM-dd"];

        NSDate *now = [NSDate date];

        NSString *todaystr = [formatter stringFromDate:now];

        NSDate *today = [formatter dateFromString:todaystr];

        

        NSDate *next = [today dateByAddingTimeInterval:24*60*60];

    //定义需要获取的数据为步数

    HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];

        

    //设置获取的步数时间间隔

    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];

        dateComponents.day = 1;

        

    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:today endDate:next options:HKQueryOptionStrictStartDate];

        

        NSString *deviceName=@"lambo";

//创建查询统计对象collectionQuery

HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:predicate options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:0] intervalComponents:dateComponents];

        collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {

            int numberOfSteps = 0;

            for (HKStatistics *statistic in result.statistics) {

                

                for (HKSource *source in statistic.sources) {

                    //HKSource对象中的name可用于区分健康数据来源--deviceName----app名称

                    if ([source.name isEqualToString:deviceName]) {

                        int steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];

                        numberOfSteps += steps;

                        

                    }

            //deviceName是根据接入的设备做的标记,

                    if ([deviceName isEqualToString:@"iPhone"]) {

                        if ([source.name isEqualToString:[UIDevice currentDevice].name]) {

                            int steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];

                            numberOfSteps += steps;

                            

                        }

                        

                    }else if ([deviceName isEqualToString:@"iWatch"] && ![source.name isEqualToString:[UIDevice currentDevice].name]){

                        if ([source.bundleIdentifier hasPrefix:@"com.apple.health"]) {

                            int steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];

                            numberOfSteps += steps;

                            

                        }

                    }else if ([deviceName isEqualToString:@"xiaomi"]){

                        if ([source.name isEqualToString:@"小米运动"] || [source.bundleIdentifier isEqualToString:@"HM.wristband"]) {

                            int steps = [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]];

                            numberOfSteps += steps;

                            

                        }

                        

                    }

                }

                

            }

            NSLog(@"ff = %d",numberOfSteps);

    //步数看这里就好

NSString *  stepString = [NSString stringWithFormat:@"%d",numberOfSteps];

            

        

            

        };

        

        [self.healthStore executeQuery:collectionQuery];

    }

    else

    {

        NSLog(@"获取步数权限失败");

    }

}];


}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值