从零开始一步步学习HealthKit:第二部分

原文链接:

http://content.catalyze.io/blog/getting-started-with-apple-healthkit-part-2

 

       随着iOS8的发布,现如今已经有很多关于苹果HealthKit的讨论,尤其是关于在应用市场里关于使用了HealthKit的app的讨论。在Catalyze中我们已经努力的扩充了许多的存储服务,并且不断的更新。这也是我们为什么去写下一篇关于HealthStoreclass的小文章。我们的CatalyzeStore能够很容易的去储存我们的数据到HealthKit中以及到Catalyze的baas服务器中。为了使用此服务,下载最新的repo代码或者更新版本至3.2。第二部分,我们将带你建立一个完整的应用,最后,你将会有一个最终的成品。学习特性数据,从HealthKit中获取数据,并且能够与保存,同步数据到HealthKit和Catalyze中。

      https://github.com/catalyzeio/RunLog 源中克隆代码。由于应用是使用了cocoa pods的,你在下载了应用之后,应该到工程根目录你执行控制台命令pod install,打开RunLog.xcworkspaceafter运行。

       如果你还没有账号,可以到Catalyze的控制面板上注册。你将在注册的时候,需要创建组织,应用名称,api关键字,和一个自定义的类,并且类名叫做health_kit 。同时需要标注phi为true,名称要依照下面格式:

Column Name

Data Type

startDate

string

endDate

string

identifier

string

value

double

units

string

      这种自定义类型的类是你HealthKit数据被保存后,利用Catalyse类进行转化的。接下来你需要做两件事,设置里的API key和你的application ID。这些被写在AppDelegate.m里,类似于: [Catalyze setApiKey:@”” applicationId:@“”]

      你也应该需要插入你的用户名和密码到你的应用里。需要注意的是你现在是处在开发状态,当你产品发布的时候,应该有一个登录界面,让用户自行使用Catalyze账户或者注册一个新的。如果你想要备份你的数据,这一步是必须的。这些代码可以放在HomeViewController.m例如:[CatalyzeUser logInWithUsernameInBackground:@”” password:@“”….

       当你完成这些步骤之后,你就可以开始你的应用程序开发了。

     

提供权限

       根据你需要的权限类型确定。

       当你在你的应用里引入HealthKit framework了之后,你需要做的是确定下来你需要向你的用户获取什么类型的数据。我建议你花时间好好想一下。有几点需要注意:第一,对于你不用的Health数据你不用去获取请求。然后,对于你不需要的数据,你也不必去获取请求。请注意你不用的数据和你不需要的数据的区别。

       本文的Runlog应用详细讲明了你不用的数据是那些。这个应用需要你提供体重,身高,同房,然后把这些数据显示在屏幕上。runlog也是一个非常好的应用,对于你不需要的数据的说明。我们现在使用的它,但是他的功能就是一个跑步记录器。通过HealthKit,我们将请求权限,并且向你展示如何使用数据,以及如何获取权限。

申请权限

       现在我们能够直观的想一下我们所需要的数据类型,我们就能够写下申请权限的类型了。找到requestHealthKitData, dataTypesToRead, anddataTypesToWrite ProfileViewController.m 文件里。后两个方法比较简单,一旦你知道语法结构,你就能够知道是如何写的了,我们来完成它:

- (NSSet *)dataTypesToRead {
    HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantityType *heartRate = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
    HKQuantityType *walkingRunningDistance = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
    HKCharacteristicType *biologicalSex = [HKCharacteristicType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
    HKCharacteristicType *birthday = [HKCharacteristicType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth];
    return [NSSet setWithObjects:heightType, weightType, heartRate, walkingRunningDistance, biologicalSex, birthday, nil];
}

view rawDataTypesToRead.m hosted with by GitHub

- (NSSet *)dataTypesToWrite {
    HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    HKQuantityType *heartRate = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
    HKQuantityType *walkingRunningDistance = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning];
    return [NSSet setWithObjects:heightType, weightType, heartRate, walkingRunningDistance, nil];
}

view rawDataTypesToWrite.m hosted with by GitHub

       通过这些代码我们会发现HealthKit在获取读写权限的时候,会需要一个HKObjectTypes类型的NSSet。这是依据HKquantityType,HKCharacteristic类型的不同来去判断的。现在我们能够获取读写权限了。把这段代码放在requestHealthKitData里。

 [_healthStore requestAuthorizationToShareTypes:writeTypes readTypes:readTypes completion:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: %@.", error);
        }
    }]; 

    这些代码将会在viewcontroller里显示用户的权限。

填充特性数据

      在第一节中,我讲述了HealthKit中的特性数据和非特性数据。runlog应用使用特性数据作为你获取权限的数据类型。现在只有同房信息和出生日期被填充。正如我们在获取权限部分看到的那样,你不能够写特性数据到healthkit中,所以这一部分就只针对特性数据进行讨论。

      第一步,就是在ProfileViewController里找到prePopulateFields方法,找到同房信息并且更新label。为了查找特性数据,你可以选择同步执行的方法。我们现在筛选同房信息,并且把这些text field以正确的数据更新。

HKBiologicalSexObject *biologicalSex = [_healthStore biologicalSexWithError:nil];
if (biologicalSex != nil) {
    switch (biologicalSex.biologicalSex) {
        case HKBiologicalSexFemale:
            _txtBiologicalSex.text = @"female";
            break;
        case HKBiologicalSexMale:
            _txtBiologicalSex.text = @"male";
            break;
        default:
            break;
    }
}

    同样的,生日信息需要正确的日期格式,筛选出用户的生日,并且更新text field。

[formatter setDateFormat:@"MMMM dd, yyyy"];
_txtBirthday.text = [formatter stringFromDate:dob];

       现在我们运行app到profile界面下我们将会看到生日、同房信息被列入了。如果你什么都没有看到,那是因为你还没有在Health app中设置它。把app关闭然后到Health app中,点击Health data tab button,然后点击Me,你见看到右上角有eidt按钮,你的界面应该看起来像这样。

healthkit-part2-1

更新你的生日,生殖健康信息信息,然后去你的runlog里看下你的Profile,你将看到这些区域里都被你刚刚更新的数据填充了。

查询

       现在我们已经有了特性数据,现在我们应该包括非特性数据了。特性数据通过查询进行恢复,在这里,我们将会使用HKSampleQuery。我们将使用两个查询,一个用于获取用户最新的体重,一个用于获取用户的身高。

填充身高和体重区域

       填充身高和体重数据几乎是相同的方法。因此我们将首先去查询身高数据。查询体重的数据直接粘贴下就好。

      首先找到retrieveHeightProfileViewController.m里,这里有一个关于完成这些task的步骤。

  1. 构造一个查询。
  2. 将查询做出限制。
  3. 仅仅获取最新的结果信息(通过end date排序)。
  4.  不去筛选这些结果。
  5.  执行查询方法。
  6. 拉下身高(体重)值作为一个double类型数据,单位是英寸(磅)在返回的例程中。
  7. 在主线程中调用text field的更新方法。

       让我们看下这些看些来是怎样的:

    HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    
    // Since we are interested in retrieving the user's latest sample
    // we sort the samples in descending order by end date
    // and set the limit to 1
    NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
    
    // construct the query & since we are not filtering the data the predicate is set to nil
    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heightType predicate:nil limit:1 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
        
        // if there is a data point, dispatch to the main queue
        if (results) {
            dispatch_async(dispatch_get_main_queue(), ^{
                HKQuantitySample *quantitySample = results.firstObject;
                
                // pull out the quantity from the sample
                HKQuantity *quantity = quantitySample.quantity;
                
                HKUnit *heightUnit = [HKUnit inchUnit];
                double usersHeight = [quantity doubleValueForUnit:heightUnit];
                _txtHeight.text = [NSString stringWithFormat:@"%@ in", [NSNumberFormatter localizedStringFromNumber:@(usersHeight) numberStyle:NSNumberFormatterNoStyle]];
            });
        }
    }];
    
    // do not forget to execute the query after its constructed
    [_healthStore executeQuery:query];

view rawPopulateWeight.m hosted with by GitHub
保存新的身高体重数据现在你运行app你将可以看到填充在text field里的最新的一些数据。如果你看不到任何数据,试着打开健康app然后到身高,体重模块下去添加一些数据。接下来我们将在我们的应用中讨论如何保存新的数据。

       显示之前的数据是一个很好地开始,但是对应于身高和体重来说,这些不是常量固定的数据。这也是他们为什么没有归类到非特性数据里。我们应该允许用户去更新他们的身高和体重数据在授权页面上。为了实现此目的,我们应该构建HKQuantitySample对象并且把他们保存在Healthkit中。

       在ProfileViewController.m中找到sorteHeight方法。这个方法将在用户在textfield里输入一些数据然后return后保存时调用,我们应该拿到身高数据,然后构建具有正确单位的例程。

    double height = _txtHeight.text.doubleValue;
    HKUnit *inchUnit = [HKUnit inchUnit];
    HKQuantity *heightQuantity = [HKQuantity quantityWithUnit:inchUnit doubleValue:height];
    
    HKQuantityType *heightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeight];
    NSDate *now = [NSDate date];
    
    HKQuantitySample *heightSample = [HKQuantitySample quantitySampleWithType:heightType quantity:heightQuantity startDate:now endDate:now];
    
    [self.healthStore saveObject:heightSample withCompletion:^(BOOL success, NSError *error) {
        [self retrieveHeight];
    }];

view rawSaveHeight.m hosted with by GitHub

       最后我们保存了例程然后调用我们之前写好的代码去更新textfield数据。体重的代码程序和这个相同。

    double weight = _txtWeight.text.doubleValue;
    HKUnit *poundUnit = [HKUnit poundUnit];
    HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:poundUnit doubleValue:weight];
    
    HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
    NSDate *now = [NSDate date];
    
    HKQuantitySample *weightSample = [HKQuantitySample quantitySampleWithType:weightType quantity:weightQuantity startDate:now endDate:now];
    
    [self.healthStore saveObject:weightSample withCompletion:^(BOOL success, NSError *error) {
        [self retrieveWeight];
    }];

view rawSaveWeight.m hosted with by GitHub

       到这时候你应该有了完整的权限页面。检查姓名首尾字母,生殖健康状况,生日,身高,体重,并且保存最新的身高体重信息,在试一下吧。

保存一条新的跑步信息

        好的,我们已经有了一个完整的权限,并且我们我们整理了一个基础的查询。在所有的查询之后,是设置数据。这些数据可以是任何数位的,但是我们只关注于我们应用中的。在那样看来我们应该将一些数据存到HealthKit里,否则我们将查询不到任何信息。

        打开NewRunLogViewController.m 然后找到saveAndFinish:方法。这个类的作用本质上是让用户保存他们最近一次的跑步信息。它收集了用户跑步的所有距离开始和结束的心率。然而我们并不是把这些数据取出直接放进HealthKit里的。我们将使用Catalyze的baas服务。当使用healthkit的时候,这个是一个非常重要的概念。你的应用在healthkit中存取的数据,可能在任意时候会被用户废除。你需要一个辅助的数据库来保存你的这些信息来防止用户废除的操作。

    我们来存取一些数据。你需要以下6个组件。

  1. An HKUnit
  2. An HKQuantity
  3. An HKQuantityType
  4. An HKQuantitySample
  5. An HKQuantityTypeIdentifier

  当用CatalyzeStore的时候,你只需要,一个组件

  1. An HKQuantityTypeIdentifier

       我们能够从healthkit中抽离出来,所以你只需要给我们一些简单的数据类型就行。单位对应的类型有些时候也需要注意。下面是如何保存一条新的runlog:

    HKUnit *bpmUnit = [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]];
    
    NSDate *startDate = (NSDate *)[_data objectForKey:kStartDate];
    NSDate *endDate = [NSDate date];
    
    NSMutableDictionary *metadata = [NSMutableDictionary dictionary];
    [metadata setValue:[NSNumber numberWithDouble:_txtBeginningHeartRate.text.doubleValue] forKey:kBeginningHeartRate];
    [metadata setValue:[NSNumber numberWithDouble:_txtEndingHeartRate.text.doubleValue] forKey:kEndingHeartRate];
    
    // send the heart beat off to Catalyze and HealthKit
    [CatalyzeStore saveQuantitySampleWithUnitString:@"mi" value:_txtDistance.text.doubleValue identifier:HKQuantityTypeIdentifierDistanceWalkingRunning startDate:startDate endDate:endDate metadata:metadata completion:^(BOOL success, NSError *error) {
        if (success) {
            
            NSMutableDictionary *metadata = [NSMutableDictionary dictionary];
            
            // this is optional to store these booleans, but it may make for easier querying later
            [metadata setValue:[NSNumber numberWithBool:YES] forKey:kBeginningHeartRate];
            
            [CatalyzeStore saveQuantitySampleWithUnitString:bpmUnit.unitString value:_txtBeginningHeartRate.text.doubleValue identifier:HKQuantityTypeIdentifierHeartRate startDate:startDate endDate:startDate metadata:metadata completion:^(BOOL success, NSError *error) {
                if (success) {
                    
                    NSMutableDictionary *metadata = [NSMutableDictionary dictionary];
                    [metadata setValue:[NSNumber numberWithBool:YES] forKey:kEndingHeartRate];
                    
                    [CatalyzeStore saveQuantitySampleWithUnitString:bpmUnit.unitString value:_txtEndingHeartRate.text.doubleValue identifier:HKQuantityTypeIdentifierHeartRate startDate:endDate endDate:endDate metadata:metadata completion:^(BOOL success, NSError *error) {
                        if (success) {
                            // all 3 completed, we're done
                            _saved = YES;
                            [self.navigationController popViewControllerAnimated:YES];
                        } else {
                            NSLog(@"error saving end heart rate");
                        }
                    }];
                } else {
                    NSLog(@"error saving beginning heart rate");
                }
            }];
        } else {
            NSLog(@"error saving distance");
        }
    }];

view rawSaveNewLog.m hosted with by GitHub

      你可以看到我们收集我么所需要的数据,存储了所有对象的元数据,也集合了开始以及结束时间的信息。这个过程之后就是简单的保存到CatalyzeStore里。随意打开CatalyzeStore.m然后看下源文件。在里面我们保存了HKQuantitySamples对象在我们看到的第一篇文章里。

显示List列表

      我们把数据利用Catalyze备份了下,也在HealthKit里储存了。现在用户打开runlog的时候,他们期望看到他们最新的一些信息。很想我们做的身高,体重那样,我们需要查询这些信息。有一个特性唯一的查询,在RunLogListViewController.m里找到queryRecentRunLogs方法,然后:

    HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierDistanceWalkingRunning]
                                                           predicate:[HKSampleQuery predicateForObjectsWithNoCorrelation]
                                                               limit:20
                                                     sortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierEndDate ascending:NO]]
                                                      resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
                                                          dispatch_async(dispatch_get_main_queue(), ^{
                                                              _runLogs = [NSMutableArray arrayWithArray:results];
                                                              [_tblRunLog reloadData];
                                                          });
                                                      }];
    // don't forget to run the query after you create it!
    [_healthStore executeQuery:query];

view rawQueryRecentLogs.m hosted with by GitHub

      如果你注意到了这个查询的方式,我们是在里面使用了predicate方法的。在身高体重的查询中我们将predicate参数设置成了nil。predicate有很多的构建方法。但是最简单的一种方法就是使用内建的query帮助去创建的。这里,我们使用predicateForObjectsWithNoCorrelation作为HKSampleQuery的predicate。这个predicate是最好的形式。大部分的结果你想得到的就是大于或者小于某个特定的值的。predicate在使用的时候,我们并不关心数据间的关联,只关注结果。不关注任何事情的获取数据结果是快速的,这种处理方式很完美。

       当你完成了这最后一个Runlog的时候,你拥有了一个完成的Runlog app。运行这个app然后开始记录一些数据吧。你可以在github上获取后续的更新。跟着这篇blog,你就是这样获取了一个完整的app。

更多的HealthKit

      对于HealthKit所有功能,苹果已经做了封装,这值得我们称赞。我们还需要更多来去学习,而且Catalyze乐于帮助开发者,让他们的开发更为便捷。如果您有任何的反馈和疑问,请给我们发邮件。

从零开始一步步学习HealthKit:第一部分

说明

本文从http://content.catalyze.io/blog/getting-started-with-apple-healthkit-part-1翻译而来,如有语法,词汇的不足,敬请批评指正,原作者保留转载,修改权力。

也许HealthKit和相关的健康应用是苹果iOS8最令人激动的更新。通过健康类的API,开发者可以将健康相关类型的数据保存在一个集中的地方,也可以访问这些数据。这给人了无限的联想空间:首先,苹果健康类型的大框架下决定了那些应用可以被下载。其次,这些健康类型的app允许用户在HealthKit Store里整体的,直观的查看到他们所拥有的数据。

这就使得用户不用担心该找那些应用去收集那些数据了,也使得开发者不用去考虑与其他应用进行通信的问题,HealthKit把这一切都给封装好了。在接下来了的两个部分中我们将讨论HealthKit的许多功能,以及使用这些功能去创建一个简单的Healthkit Demo。今天的第一部分我们将整体上了解下HealthKit的一些接口,提供一些代码片段等等,在下一节我们讲着重讲解下更多的技术细节问,进而构建出一个完整的App 例子。

关于许可

利用HealthKit,苹果将用户的健康数据保存的非常的精细化。在开发者使用HealthKit之前,你需要获取你能够读写数据类型的权限。例如你只想列出用户的生日和体重信息,想记录用户的体温信息,那么你就得对生日和体重获取只读权限,对温度获取读写权限。

一旦获取读取权限成功,用户手机上将会显示一个允许那些类型的数据被存取的对话框,并且对一些单一的功能提供授权。如果你期望获取写入的权限,那么你将在你的应用里无法获取数据被更改的通知。然后,当你期望获取读取的权限的时候,将不会发送授权的通知,或者用户在弹出Alert通知的时候,选择了取消。

隐藏用户的请求能够有效的保护用户的信息,例如,当你想要记录用户的血糖值并且用户拒绝的时候,你就能假定用户是患有糖尿病的。保护这些信息是苹果HealthKit保护终端用户隐私的一个重要的地方。

在HealthKit中,所有的许可都在HKHealthStore里。在你的应用中,最好只保存一个HKHealthStore的实例,这样就能够只用向用户提出一次的权限访问请求,否则将会不断的向你的应用提出读取权限请求,导致你的应用程序混乱。

同步HealthKit的数据将对你的健康类型的应用的功能进行削弱。因此在不进行任何详细说明的前提下去获取HealthKit 的数据是非常不明智的,这是个非常好的练习的机会,尤其是你在有用户登录的时候,先让用户登录应用,进而去熟悉下你的应用。了解那些类型的数据将被收集。然后可能将在第一时间获取存取权限,一旦你正确的获取了读取权限了之后,你就能够开始收集相关的数据点了。

关于单位

HealthKit中最基本的构造就是测量单位,你能够无限制的向用户收集数字,但是这些数字离开单位的后缀是毫无意义的。HealthKit中与单位相关的类是HKUnit,这个类使用起来非常的直接,并且已经包括很多相关的单位类型,能够快速的帮你创建你想要的单位。如果没有帮助信息找不到你想要的单位,你可以通过以下方式去获取你想要的单位类型:

HKUnit *gram = [HKUnit gramUnit];

HKUnit *mgPerdL = [HKUnit unitFromString:@"mg/dL"];

 

单位的选择应当贴合你的数据类型。例如,你不会选择克作为测量温度的单位,并且也不太可能选择开氏温度单位去度量体温。作为体温比较适合的单位只能是华氏或者摄氏比较好些。这个根据用户的第一选择来去确定,HealthKit不参与。HealthKit中也有一些将公制的单位与其他系统制式的单位进行转换的API,这篇文章中不做讨论。

关于数量

现在我们构建好了单位,也同时收集了一个数字,那么我们现在就得把两者进行关联下。在HealthKit中关联一个值与单位的类就是Quantity。以下是HKQuantity类型的构建:

HKQuantity *quantity = [HKQuantity quantityWithUnit:gram doubleValue:120.0];

 

虽然简单,但是quantity是非常强大的,而且也是单位换算的基础。

单位换算

单位换算中两个直接关联的类是HKQuantity和HKUnit。当然你不能够把任意类型的单位转移到另外的任意类型上,那样子的话会被系统抛出一个异常。你可以选择询问HealthKit是否允许转换,然后在去进行转换的工作:

[quantity isCompatibleWithUnit:[HKUnit unitFromString:@"kg"]]; //this one returns YES

double kg = [gram doubleValueForUnit:[HKUnit unitFromString:@"kg"]];

 

例程

我们从用户那里获取了数据,单位,以及数量,那么我们现在就要创建一个保存在HealthKit数据上的对象。在HealthKit中,只有通过例程去存储相应的HealthKit对象。有两种主要的类型:HKQuantitySample和HKCategorySample,两种都是HKSample的子类。需要注意的是HKSample就想其他的HealthKit类型一样是HealthKit的子类,但是本文例子中没有对这个类的相关的介绍。以下是4中类型的类型关系图:

HKObjectHierarchy@2x

数量例程是对于数据的一个度量以及数量的一个度量。这包括血糖的指标,体重,心率,体温等等。分类例程是指一些能够放进类型里的一些度量。现如今只有一种类型的分类例程,那就是睡眠质量。你能够自行分配一个例程例如熟睡时间在床上,或者用户睡眠状态。由于数量例程被使用的更多,我们详细看下如何创建HKQuantitySample:

HKQuantitySample *mySample = [HKQuantitySample quantitySampleWithType:bloodGlucoseType quantity:quantity startDate:now endDate:now metadata:nil];

这段代码告诉了我们数量例程的样子(分类例程也是如此),它由一个类型,一些数量(或者一些指标对于分类信息而言),一个开始的时间,一个结束的时间,当然还有一些元数据。开始/结束时间标注了那些数据应该被收集。对于一些即时获取的数据例如血糖的值,开始时间和结束时间是相同的。它们都是和采集时间相同的。另一个比较简单的概念是元数据,这里包含了任何你想要保存的关于这个例程的信息。这里的值是一个dictionary,但是只能够保存key,string,number这些数据。

元数据和一些催化模型的额外数据很像,但是在例程中最有趣的组件是类型和数量。

类型

类型可以说是例程中最为重要的组件。离开例程你所有的全部仅仅只是一些数量以及一些日期。因此,也许你知道你的例程是:120mg/dL,记录日期09/09/2014。对于你我来说这些就是针对于血糖信息的。但是对于电脑来说,它仅仅只是一个简单的数字,一个单位,以及一个日期。类型可以告诉HealthKit正在测量的数据类型。它给予了健康数据正确的内容和意义,它是 120mg/dL的血糖值,或者它是98.6˚F体温值。再搭配单位的属性,你的数据将会更加的有意义。正如不同的例程一样,类型的种类也可以是不同的。HKQuantityType和HKCategoryType都是HKSampleType的子类。与之相同的还有HKCharacteristicType。HKCharacteristicType和HKSampleType都是HKObjectType的子类,以下是类型关系图:

HKObjectTypeHierarchy@2x

数量和类别在Sample同一个分支下。如果你有个血糖的例程(是HKQuantitySample类型的),你可能需要一个HKQuantityType的类。数量类型已经被标示符初始化,是最好的例子。如果你的例程是一个血糖的例程,当然是一个HKQuantitySample的例程,你的类型应该是HKQuantityType类型,然后你的标示符应以HKQuantityTypeprefix。完整的标示符是HKQuatityTypeIdentifierBloodGlucose

HKQuantityType *bloodGlucoseType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodGlucose];

Similarly for category types

HKCategoryType *sleepAnalysisType = [HKCategoryType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis];

特性例程是一种新的例程类型。这种类型包括了同房、生日、血型等信息。这些数据不被经常性的改变。HealthKit针对这些数据与其他的数据不同,是不存在多重拷贝的。而且,对于用户来说,可能仅仅只是想恢复最新时间的信息。对于这些,HealthKit提供了分开的方式处理保存和取回数据。这些将在第二个部分详细讲到。

不同于特性例程数据,所有健康类数据必须结构化保存数据,单位和类型信息也应该被保存下来。

储藏数据

我们将我们所有要保存的保存在健康数据库中。调用saveObject:withCompletion:在HKHealthKitStore对象里就ok。很简单,你仅仅需要的就是一个例程。下面是图例关于这些关系的说明,这个可以以血糖值作为参考,当然,这个是HKQuantitySample类型的。

HealthKitSampleComposition

完成block

通过完成Block方法,HealthKit通常会有一个异步执行的进程来告诉你什么时候保存完成了。几乎所有的Block运行都是在后台进程执行的。因此,当你在完成Block里执行UIKit相关操作的时候,你需要跳转到主线程里去执行。教程第二部分将会对此进行详尽的解释,UIKit相关的执行必须放在主线程里是iOS系统的一个重要规定。

疑问

ok,我们现在保存了一些数据,也许你保存了许多。现在的问题是我们需要拿回这些数据。考虑下这种情况,病人通过几个月的血糖app获取到了一些数据,根据这些数据去看医生,有可能被问到,你什么时候的血糖值是超过140mg/dL?或者是你能够给我看下最后一次的病例吗?我们的病人通常都是会容易忽略这些,并且不容易回想起最后一次的病例。但是用户使用的血糖app就能够有效的做到这一点。

非特性数据

非特性数据是查询中最基本的一个类型,他的结果就是根据既定的参数返回所有的结果。所有的queries继承自HKQuerywhich并且具有两种属性:sampleType和predicate。same type是正在查询的数据类型。这是HKQuantityType(或者HKCategoreType等等)的一个实例。predicate是确定要返回那些值所提供的一些参数。predicate有些许不同的定义方式,我们将会在第二节着重讲到。通过上面的例子,我们就能够筛选出所需要的一些血糖数据。

查询Queries方法是能够有限制数量和排序功能的。限制数量同MySQL中的LIMIT或者其他的数据库中一样,给返回来的数据提供数量的限制,不做数量限制的话可以使用HKObjectQueryNoLimit这个关键字。

排序功能有些麻烦,他是NSSortDescriptors中的一个数组,如果你重来都没有用过NSSortDescriptors你不必过虑,因为他们比较难弄清楚,需要时间。下面是NSSortDescriptors的一个使用例子:

[NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierEndDate ascending:NO];

这个例子是说明数据根据时间降序排列。你也可以使用HKSampleSort去看下结果是怎样。

特性数据

特性数据有几个方法将在第二部分中讲到。

观察者Queries方法

对于Query查询的比较行之有效的一个方法是观察者模式。如果你想要查询HealthKit中的某个数据你可以通过轮寻最新的数据这中方式去查找,但是这种方式比较耗费资源,并且不够优雅。因此可以使用HKObserverQuery方法。观察者模式是一个长久执行的Query并且不会被随意停止。和完成block一样,observer queries能够通过一个句柄去不断检测数据的变化。

这种方式的处理能够有效的处理软件与硬件之间的配合。如果用户正在使用一个穿戴式的设备检测心率,那么你就能够在应用中实时的展现这些数据。这是一种非常好用的工具。

拓展—Catalyze

通过Catalyze,我们能够很好的帮助你学习HealthKit。我们已经开发了一套utility能够帮助你顺利的实时的保存数据到HealthKit和我们的HIPAA APIs中。那样子的话,无论用户数据中发生了什么样子的数据保存,他们的数据将会同步到外部一个固定的地方,并且对应的是能够让用户进行读取的。另外,如果用户吊销了与HealthKit之间的存取权限,你将无法恢复数据,并且应用也不能够写入数据。这样你就丢失了所有的历史数据。我们的这种不会受到影响。通过这个工具,你能恢复和存取任何数据,完全尊重用户的隐私。

通过第二部分的讲解,你就能够开始通过HealthKit进行开发了。我们非常期待你的交流,如果你有任何反馈和疑问,请通过邮件同我们交流。