in Health Kit ~ read.

Health Kit教程(一)

引言

Health Kit是在iOS8中才出现的一个新的特性,是用来提供存储和获取用户健康数据的一个苹果自带的数据中心。而本篇文章的核心就是提供一个Health Kit的教程性知识。而对于Health Kit的教程将用两篇文章的篇幅进行讲解。通过这两篇文章,小伙伴们将会学到关于Health Kit的一些基础使用。
而本片文章是在Xcode 7.1 / Swift 2.0的基础上进行开发的。

准备好开始Health Kit之旅了吗?那么开始吧!

开始

那么在开始之前,首先对本片文章所要叙述的内容进行大体介绍一下。本篇文章主要讲解一下几个知识点: * 获取Health Kit 的授权 * 读取用户属性 * 读取或写入用户特性 鉴于编程的学习都是需要建立在编码的基础上,那么小伙伴都新建好新的Health Kit的项目了吗?如果准备好的话,那我们就继续往下走吧。

获取Health Kit的授权

授权之配置篇

在开始编程之前,我们肯定要对项目中关于获得Health Kit的授权进行一些基础的配置,保证我们之后在写代码过程中畅通无阻。 那么首先先填好Bundle Identifier以及选择好对应的Team,具体如下图 Bundle Identifier And Team 在这需要到target的General中填写好对应的渠道名以及选好对应的开发者 然后就是就是添加对应授权文件(即entitlement文件),其实不需要我们手动去添加,只需Capabilities中打开HealtKit的开关为On即可,然后稍等片刻后即可。如下图: Add HealtKit Entitlement

注意:这里有一点强烈建议小伙伴们需要注意的一点就是,如果小伙伴的程序是兼容iOS 8一下或者需要支持iPad之类等不支持Health Kit的设备的话,需要到Info.plist文件中删除Required device capabilities下的healthkit的值,否则如果在iPad或者iOS7上安装不上,博主可是不负责的哟!

好了关于配置方面的,就到此为止了,接下来小伙伴就可以开始愉快的编程了。

授权之代码篇

在此之前,博主的Demo中是为HealthKit的操作添加了一个Util的工具类,命名为HealthKitUtil.swift。打开这个文件首先当然得import HealthKit了。然后关于Health Kit的数据处理都要用到HKHealthStore这个对象的,就如引言中所说的,Health Kit其实就是一个数据中心,主要用来存储和读取用户的健康信息。而HKHealthStore就像是一个数据库的数据库处理对象一样,所以在我们对Health Kit的数据进行操作的过程中都需要通过HKHealthStore的对象。所以在我们HealthKitUtil工具类里面就会存在一个其的属性。 那么接下来在获得Health Kit授权之前,我们首先得要知道我们对Health Kit的需要哪些读的权利以及哪些写的权利,毕竟我们有的时候是不需要对Health Kit的所有数据类型都进行操作的。例如对于一个智能秤相关的App可能只对体重,脂肪率以及BMI等一些跟身体相关的数据关心,而对于运动的一些数据则不进行关心。 而对于Health Kit的数据类型都是HKObjectType的子类,并且在HKObjectType中也提供了产生对应子类的方法,只需执行对应的方法即可获得对应的操作类型(如血型等)。具体方法如下:

+ (nullable HKQuantityType *)quantityTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCategoryType *)categoryTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCharacteristicType *)characteristicTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCorrelationType *)correlationTypeForIdentifier:(NSString *)identifier;
+ (HKWorkoutType *)workoutType;

可以看得出来,大部分的操作类型都是通过一个叫identifier的来进行产生的,而具体包含了那些操作类型呢,其实可以通过Health Constant Reference来查看。从中找到你需要读和写的操作类型。然后调用HKHealthStorerequestAuthorizationToShareTypes(typesToShare: Set<HKSampleType>?, readTypes typesToRead: Set<HKObjectType>?, completion: (Bool, NSError?) -> Void)方法即可。具体代码如下:

    class func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -> Void)!) {
        // 1. 想要读的操作类型
        let readTypes : Set<HKObjectType> = [
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType)!,
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!,
            HKObjectType.workoutType()
            ]

        // 2. 想要写的操作类型
        let writeTypes : Set<HKSampleType> = [
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!,
            HKQuantityType.workoutType()
            ]

        // 3. 查看在该设备上HealthKit是否可用
        if NSClassFromString("HKHealthStore") != nil && !HKHealthStore.isHealthDataAvailable() {
            let error = NSError(domain: "com.pluto-y.healthkittotorial", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit在该设备上不可用"])
            if( completion != nil ) {
                completion(success:false, error:error)
            }
            return;
        }

        // 4. 获取授权
        store.requestAuthorizationToShareTypes(writeTypes, readTypes: readTypes) { (success, error) -> Void in
            if completion != nil {
                completion (success: success, error: error)
            }
        }
    }

添加这个代码后,在视图中添加一个授权按钮进行调用该方法。至此就成功获得Health Kit的授权了。

读取用户特征

在获取授权之后,当然先开始一个简单的就是获得用户的特征,关于用户特征就是在Health Kit中之允许读的数据,在完成这个教程的时候,关于这个特性包含了四项,分别是血型、性别、生日以及皮肤类型。而在这个教程中主要用到的就只有生日,血型和性别三个。如果在测试前小伙伴还没有填写过特征的话,可以去Health Kit中填写,保证后面的demo能正常运行。 回到工具类中,通过添加以下方法来进行读取特性:

    // 读取特征
    class func readCharacteristics() -> (age : Int?, biologicalSex : HKBiologicalSexObject?, booldType : HKBloodTypeObject?) {
        var age : Int? = 0
        // 读取生日
        if let birthday = try? store.dateOfBirth() {
            let today = NSDate()
            let differenceComponents = NSCalendar.currentCalendar().components(NSCalendarUnit.Year, fromDate: birthday, toDate: today, options: NSCalendarOptions(rawValue: 0))
            age = differenceComponents.year
        }
        // 读取性别
        let biologicalSex : HKBiologicalSexObject? = try? store.biologicalSex()
        // 读取血型
        let bloodType : HKBloodTypeObject? = try? store.bloodType()
        print("age : \(age), biologicalSex: \(biologicalSex), bloodType: \(bloodType)")
        return (age, biologicalSex, bloodType)
    }

从上面的代码可以看得出来对于用户的特征来说的话,读取是一件非常简单的事情,就好像是在读取一个实例的属性一样。而之后只需在视图中添加一个按钮和现实结果的视图进行演示这个结果即可。

读取和写入用户简单数据

相对于用户特征来说,用户数据则是通过各种设备、第三方App或者用户手动输入测量数据。而这些数据一般都是连续的数据,不像是用户特征,在Health Kit中只存在一个。对于像身高、体重、BMI等一些数据可能随着时间的变化都会有不同的数据。而在这个小节中,博主给大家先讲述如果简单的读取最近的一个身高以及体重,并根据读取到的数据计算出对应的BMI,再将这个计算后的BMI存入Health Kit中(如果在Health Kit中没有身高和体重数据点的童鞋可以先去Health Kit中写入两个数据点)。

读取身高和体重

首先先来读取Health Kit中的身高和体重数据,通过这节后小伙伴们可以读取对应的其他类型的数据。其实对于微信、QQ等社交工具中有个计步排行榜也是通过读取Health Kit来读取的,类似的小伙伴也可以通过这节学到如何读取其他数据的方法,只需在读取的时候选择对应的数据类型即可。废话不多说,上代码:

    class func readMostRecentData(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -> Void)!) {

        // 1. 产生一个相对相对久远的时间到单签时间的时间段(用于读取数据的时间段)
        let timeBegin = NSDate.distantPast()
        let timeEnd   = NSDate()
        let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(timeBegin, endDate:timeEnd, options: .None)

        // 2. 产生一个排列的过滤器,即按照什么来排序
        let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
        // 3. 取的条目数,即我们要从Health Kit中取多少条数据
        let limit = 1

        // 4. 产生一个从Health Kit中获取数据的Query对象,HKHealthStore通过该对象进行取得符合条件的数据
        //    关于最后一个参数闭包是用于获取数据后的回调
        let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
            { (sampleQuery, results, error ) -> Void in

                if let _ = error { // 读取过程中出错
                    completion(nil,error)
                    return;
                }

                // 读取第一条数据
                let mostRecentSample = results!.first as? HKQuantitySample

                // 将第一条数据传给最后一个参数的闭包中
                if completion != nil {
                    completion(mostRecentSample,nil)
                }
        }
        // 5. 执行读取Health Kit的操作
        store.executeQuery(sampleQuery)
    }

上述代码是在Health Kit工具类添加的方法,用来读取指定类型数据最近时间的一条数据的工具方法。其中HKSampleType则为HKObjectType的子类,可以通过HKObjectType提供的根据identifier来获得具体需要获取数据的类型。而在获取数据时候需要三个参数,分别是: * 获取数据的时间段,即需要获取数据时间过滤器,只有在这个时间段内的数据才能被取到。在该工具方法中,随机产生了一个相对当前时间往前相对比较久远的时间段,这样能保证去到一个有效的数据 * 获取数据的排序字段,即根据那个字段来进行排序,具体可以排序的字段可以通过HKSample类以及子类来查看可排序字段。在该工具方法中,都采用按照时间逆序来排序 * 获取条目数,即要获取的条目数,如果不希望指明条目数的话可以传递HKObjectQueryNoLimit即可。在该工具方法中,只获取距离当前时间最近的一条数据

既然有了数据的方法,可以通过一下方法来进行读取:

    // 1. 指明读取类型
    let dataType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!

    // 2. 调用工具类的方法来获取体重
    HealthKitUtil.readMostRecentData(dataType, completion: { (mostRecentWeight, error) -> Void in

    if( error != nil ) { //获取体重过程中出现错误
      print("读取体重过程出错  error: \(error.localizedDescription)")
      return;
    }

    var weightLocalizedString = "Unknow";
    // 3. 将读取到的体重信息进行处理后再显示
    self.weight = mostRecentWeight as? HKQuantitySample
    if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) {
      let weightFormatter = NSMassFormatter()
      weightFormatter.forPersonMassUse = true;
      weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)
    }

    // 4. 更新界面上的显示
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.yWeightLbl.text = weightLocalizedString
      });
    });

可以通过上方的方法进行读取体重与身高,并将获取到的数据进行基本处理后在显示到界面上。其中关于获取到的数据,其类型为HKQuantitySample的数据类型,而该数据类型包含了两个属性变量HKQuantityTypeHKQuantity两个实例变量的属性,前者用来保存数据的类型信息,后者则用来保存数据的值以及单位信息。而其中上述代码中的主要是针对数值信息进行转换成本地单位后再进行显示。

而这里设置到了在Health Kit中添加的HKUnit类来进行单位的管理,其中包含了许多单位,如重量,长度、压力等等等。而我们可以通过具体需求来显示不同的单位,并且其中也内置了具体的转换方法,不需要我们自己去将单位进行转换。具体的内容可以查看HKUnit的头文件进行学习。

保存BMI数值

有了身高和体重之后就可以计算BMI了,并且在Health Kit中添加一个BMI的数据点。那么在工具类中添加以下代码来进行保存一个BMI的数据点,小二上代码:

// 保存BMI数据点
class func saveBMISample(bmi:Double, date:NSDate ) {

    // 1. 创建数据点
    let bmiType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)
    let bmiQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: bmi)
    let bmiSample = HKQuantitySample(type: bmiType!, quantity: bmiQuantity, startDate: date, endDate: date)

    // 2. 保存数据点
    store.saveObject(bmiSample, withCompletion: { (success, error) -> Void in
        if( error != nil ) {
            print("报错BMI数据点出错 error:: \(error?.localizedDescription)")
        } else {
            print("保存BMI数据点成功!")
        }
    })
}

该工具方法的第一个参数BMI的数值,第二个参数则为保存在本地BMI值的时间。而可以看出来了在第一步骤中证明了上方说的HKQuantitySample包含了一个数据类型的和数据的值的信息的两个属性。而第二步中,则将数据点存入到Health Kit中去,并且根据是否成功输出不同的信息。

简单数据小结

可以看出简单数据的读取和存入就很像我们平时操作数据库的习惯。但是跟数据库操作不同的地方在于数据库操作中我们需要知道数据库表的结构,要知道如何获取语法等。而对于Health Kit操作这些数据点的方法则是对数据库的底层进行封装,并且将封装后的可操作的接口和字段进行公开。这样我们就不需要知道具体的表结构以及语法了。我们只需根据API中提供的字段和方法进行操作即可。 在这里还可以告诉小伙伴们,其实对于iPhone中的微信和QQ的计步排行榜就是从Health Kit中读取的,那么小伙伴们当然可以通过Health Kit提供的API写入自己想要的Health Kit的步数咯。比如今天想写12345步,明天想写123456步都可以随心所欲,那么每天第一名不就是妥妥的么。然后你就义正言辞的发个说说:"你追我,如果你知道我,我就让你嘿嘿嘿"。每天都写超级多不,保证肯定没有人能追的上你。 嘿嘿嘿

本文小结

好了,最后来总结一下吧。这篇教程教了一些入门方面的操作和编程。对于一些简单的操作只要按照这篇博问里面进行操作即可。至于锻炼Workout这种类型的数据操作将在下篇博客中进行讲解,大家尽请期待。看看入门Demo的运行情况: HealthKitDemo1.gif

至于Demo的话大家可以到我的Github上下载来研究研究,如果有什么问题或者有什么错误的地方麻烦大家指出。

comments powered by Disqus