是 Apple 公司在推出 iOS 8 系统时一块推出的关于健康信息的框架。如果 iPhone 手机系统升级到 iOS8 之后就会发现多了一个
- HeathKit
, 这就是 Apple 提供的一个记录用户健康信息的 app, 可以用它来分享健康和健身数据。还可以指定数据的来源,比如我们自己创建一个 app, 在我们的 app 中使用了
- 健康-app
框架之后只要经过用户的认证,就可以在我们的 app 之中给
- HeathKit
分享数据或者从
- 健康
中获取数据。
- 健康
可以与健身设备一起工作,iPhone 手机自身可以监控步数信息,会自动导入步数信息。但是其他信息或者设备需要配套的应该才能获取到数据并导入到
- HeathKit
中并在
- HeathKit
中显示。
- 健康
不能再 iPad 中使用,而且它也不支持扩展。
- HeathKit
由于用户的健康信息可能是敏感的,所以这些用户信息不能让开发者很随便的获取到。每条信息的读写都需要用户去选择是否同意,比如用户可以同意你获取到用户的身高体重,但是不同意读写生殖健康等其他用户不愿意公开的信息。为了防止信息泄露,我们是不知道用户是否禁止了某条信息是否被用户禁止读取的。简单的说,如果获取不到某条信息,就代表没有这条信息。
关于更多的关于隐私的信息,可以参考
在各个应用之间提供了一种有意义的方式共享数据。因此,我们必须使用
- HeathKit
框架提供的数据类型和单位。这保证了数据存在的真正意义,我们不能自定义数据类型及单位。框架使用了子类化,例如
- HeathKit
和
- HKObject
抽象类拥有很多有平行关系的子类,当使用
- HKObjectType
或者
- Object
的时候,必须确保使用正确的子类。
- ObjectType
在
中能够存储的类都是
- HeathKit
的子类,大部分
- HKObject
的子类都是不可变的。每个对象都有下面的属性:
- HKObject
的健康 app, 也可以是我们自己创建的 app。当一个对象存储到
- HeathKit
中时会设置其来源。只有从
- HeathKit
中获取到的数据的来源才有效。
- HeathKit
的对象主要分为特征和样本。特征对象代表用户的基本不变的数据,包括用户的生日、血型和性别等。我们创建的 app 不能修改这些信息,只能让用户在
- HeathKit
中去修改或者添加个人特征信息。 样本对象代表某个特定时间的数据,所有的样本类型的对象都是
- 健康
的子类。它们都有下面的特性:
- HKSample
样本类型又可以分为四个类型:
):在 iOS 8 中,只有睡眠分析这一个类别样本。代表有限种类的样本.
- HKCategorySample
):这种样本代表存储数据的样本,比如步数、距离、用户的体温等。它是
- HKQuantitySample
中最常见的数据类型。
- HeathKit
):代表复合数据,包括一个或者多个样本。在 iOS 8 中,用
- HKCorrelation
代表食物和血压。在创建食物或血压时,需要用
- correlation
。
- correlation
):代表某种活动,比如走、跑步等。包含有开始时间、结束时间、运动类型、消耗能量、运动距离等属性。还可以为
- HKWorkout
关联许多详细的样本。不像
- workout
,这些样本不包含在
- correlation
中,但是可以通过
- workout
获取到。
- workout
再介绍一个 HeathKit 中经常用到的一些类。
每个
的子类都有对应的便利方法创建对应的对象。比如:
- HkSample
对于数量样本,需要创建
类的实例。而且数量的单位和类型标识符文档中描述的可用单位要相同。例如:
- HKQuantity
文档中说明它使用长度单位,因此,你的数量必须使用厘米、米、英尺、英寸或者其他长度单位。
- HKQuantityTypeIdentifierHeight
对应类别样本,需要创建
的实例。它的值必须和类型标识符文档中描述的枚举值相关。例如,
- HKCategorySample
s 文档中说明它使用的枚举值。因此你在创建样本时必须从这个枚举中传递一个值。
- HKCategoryTypeIdentifierSleepAnalysi
同样,你必须先创建 correlation 包含的所有样本。correlation 的类型标识符描述了它可以包含的类型和对象的数量。不要把被包含的对象存进 HealthKit。它们是以 correlation 的一部分存储的。
对于训练活动样本,首先,创建
实例并不需要指定类型标识符。所有的 workout 都是用同样的类型标识符。第二,对于每个
- HKWorkoutType
你都需要提供一个
- workout
值。这个值定义了
- HKWorkoutActivityType
中执行的活动的类型。最后,当
- workout
保存到
- workout
后,你可以给
- HealthKit
关联额外的样本。这些样本提供了
- workout
的详细信息。
- workout
HeathKit 提供了许多查询读取数据的方法:
这个类代表要查询的数据的单位的类,比如体重的单位,可以为 kg、lbs 等。这个类为不同的数据类型提供了不同的单位方法。一般在创建前面介绍的样本类型的时候,都需要这个类为样本添加对应的单位。而且提供了一些数学运算,比如千米、米、厘米等之间的转换。
在某些场合,你可以使用格式化器来本地化数量。iOS8 提供了提供了新的格式化器来处理长度(
)、质量(
- NSLengthFormatter
)和能量(
- NSMassFormatter
)。对于其他的数量,你需要自己来换算单位和本地化数据。
- NSEnergyFormatter
HeathKit 的核心就是它,它代表 HeathKit 的数据库,使用它就可以从数据库中读取数据。比较重要的方法:
在使用
之前,必须要执行下列步骤:
- HealthKit
, 在 Target 栏中,打开
- HeathKit
菜单,将
- Capabilities
这一项的开关设为 ON 的状态。
- HealthKit
文件,并导入
- HeathManager.Swift
- `import HeathKit`
的核心是
- HeathKit
,创建
- HeathStore
然后调用在这个方法中调用
- func authorizeHealthKit(completion: ((success: Bool, error: NSError ! ) - >Void) ! ) {}
判断当前设备是否支持
- isHealthDataAvailable
- HeathKit
- //判断当前设备是否支持
- if ! HKHealthStore.isHealthDataAvailable() {
- let error = NSError(domain: "", code: 2, userInfo: [NSLocalizedDescriptionKey: "HealthKit is not available in this Device"]) if completion != nil {
- completion(success: false, error: error)
- }
- }
- let hkHealthStore = HKHealthStore()
来认证请求从 HeathKit 获取数据的权限。
- requestAuthorizationToShareTypes:readTypes:completion:
如果当前设备支持 HeathKit 的时候,这样就会弹出一个请求界面,让用户选择是否同意你能够获取到你要请求的数据。
- //请求连接
- hkHealthStore.requestAuthorizationToShareTypes(healthKitTypesToWrite as ? Set < HKSampleType > , readTypes: healthKitTypesToRead as ? Set < HKObjectType > ) { (success, error) - >Void in
- if completion != nil {
- completion(success: success, error: error)
- }
- return
- }
我们首先创建了
, 并用 IB 创建一个请求个人信息的界面
- ProfileViewController.swift
然后在
文件中添加请求个人信息的方法。 对于请求特征信息,前提上用户通过
- HeathManager.Swift
添加了出生日期、性别、血型等特征信息
- 健康
- func readProfile() - >(age: Int ? , biologicalsex: HKBiologicalSexObject ? , bloodType: HKBloodTypeObject ? ) {
- //请求年龄
- var age: Int ? let birthDay: NSDate;
- do {
- birthDay =
- try hkHealthStore.dateOfBirth() let today = NSDate() let diff = NSCalendar.currentCalendar().components(.Year, fromDate: birthDay, toDate: today, options: NSCalendarOptions(rawValue: 0)) age = diff.year
- } catch {}
- //请求性别
- var biologicalSex: HKBiologicalSexObject ? do {
- biologicalSex =
- try hkHealthStore.biologicalSex()
- } catch {}
- //请求血型
- var hkbloodType: HKBloodTypeObject ? do {
- hkbloodType =
- try hkHealthStore.bloodType()
- } catch {}
- return (age, biologicalSex, hkbloodType)
- }
请求体重、身高、BMI 的时候,创建另外的方法。
- func fetchMostRecentSample(sample: HKSampleType, competion: ((HKSample ! , NSError ! ) - >Void) ! ) {
- //1.创建谓词
- let past = NSDate.distantPast() let now = NSDate() let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate: now, options: .None)
- //2.创建返回结果排序的描述,是降序还是升序的,因为只需要一个结果,就设定限制为1个
- let sortDescrptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false) let limit = 1
- //3.创建HKSampleQuery对象,
- let sampleQuery = HKSampleQuery(sampleType: sample, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescrptor]) { (sampleQuery, results, error) - >Void in
- if let queryError = error {
- competion(nil, queryError) return
- }
- let mostRecentSample = results ? .first
- if competion != nil {
- competion(mostRecentSample, nil)
- }
- }
- //4.执行查询
- self.hkHealthStore.executeQuery(sampleQuery)
- }
获取之后在之前创建的
文件中获取这些信息,并更新 UI。
- ProfileViewController.swift
对应特征信息,可以直接调用查询方法,并更新
- let profile = healthManager ? .readProfile() self.healthStore = HKHealthStore() ageLabel.text = profile ? .age == nil ? kUnKnowString: String(profile ! .age ! ) sexLabel.text = biologicSexLiteral(profile ? .biologicalsex ? .biologicalSex) bloodTypeLabel.text = bloodTypeLiteral(profile ? .bloodType ? .bloodType)
这里面创建了两个工具方法
和
- biologicSexLiteral
来修改查询的结果为我们想要的样子并显示在界面上。
- bloodTypeLiteral
对于体重和身高,需要创建样本查询
- /**
- 获取并更新体重
- */
- func updateWeight() {
- let weightSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass) self.healthManager ? .fetchMostRecentSample(weightSampleType ! , competion: { (mostRecentSample, error) - >Void in
- if error != nil {
- return
- }
- var weightString = self.kUnKnowString self.weight = mostRecentSample as ? HKQuantitySample
- //根据我们想要的数据类型单位获取对应的结果
- if let kilograms = self.weight ? .quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(HKMetricPrefix.Kilo)) {
- //体重格式化
- let weightFommater = NSMassFormatter() weightFommater.forPersonMassUse = true weightString = weightFommater.stringFromKilograms(kilograms)
- }
- //因为这个查询默认是异步查询的,所以需要在主线程更新UI
- dispatch_async(dispatch_get_main_queue()) { () - >Void in self.weightLabel.text = weightString self.updateBMILabel()
- }
- })
- }
- /**
- 获取并更新身高
- */
- func updateHeight() {
- //设置要查找的类型,根据标识符
- let heightSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)
- //获取身高样本
- self.healthManager ? .fetchMostRecentSample(heightSampleType ! , competion: { (heightSample, error) - >Void in
- if error != nil {
- return
- }
- var heightStr = self.kUnKnowString self.height = heightSample as ? HKQuantitySample
- //根据我们想要的数据类型单位获取对应的结果
- if let kilograms = self.height ? .quantity.doubleValueForUnit(HKUnit.meterUnit()) {
- heightStr = String(format: "%.2f", kilograms) + "m"
- }
- //因为这个查询默认是异步查询的,所以需要在主线程更新UI
- dispatch_async(dispatch_get_main_queue()) { () - >Void in self.heightLabel.text = heightStr self.updateBMILabel()
- }
- })
- }
对应 BMI, 它代表人的身体质量指数,它的计算方式是: 体重 /(身高 * 身高)。因此它可以这样获得
- /**
- 获取并设置BMI:
- */
- func updateBMILabel() {
- //根据我们想要的数据类型单位获取对应的结果
- let weight = self.weight ? .quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(HKMetricPrefix.Kilo)) let height = self.height ? .quantity.doubleValueForUnit(HKUnit.meterUnit()) var bmiValue = 0.0
- if height == 0 {
- return
- }
- dispatch_async(dispatch_get_main_queue()) { () - >Void in bmiValue = (weight ! ) / (height ! *height ! ) self.BMILabel.text = String(format: "%.02f", bmiValue)
- }
- }
在下面的方法中添加一个
让用户输入 BMI 值,然后点击确认按钮之后添加到
- alertView
中
- HeathStore
- @IBAction func addBMIData2HealthStore(sender: AnyObject) {
- let alertView = UIAlertController(title: "输入BMI值", message: nil, preferredStyle: .Alert) alertView.addTextFieldWithConfigurationHandler { (textField) - >Void in textField.keyboardType = .NumberPad
- }
- let action = UIAlertAction(title: "添加", style: .Default) { (action) - >Void in
- var value: Double ?
- if let text = alertView.textFields ? .first ? .text {
- if text.characters.count > 0 {
- value = Double(text) self.saveBMI2HealthStore(value ! )
- }
- }
- }
- alertView.addAction(action) self.presentViewController(alertView, animated: true, completion: nil)
- }
- //保存BMI到heathKitStore中
- func saveBMI2HealthStore(height: Double) {
- //BMI的类型
- let BMIType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)
- //根据标识符对应的单位创建BMI的数量对象
- let BMIQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: height) let now = NSDate()
- //根据起止时间以及上面创建的创建HKQuantity对象创建数量样本
- let BMISample = HKQuantitySample(type: BMIType ! , quantity: BMIQuantity, startDate: now, endDate: now)
- //保存数量样本到healthStore中
- self.healthStore ? .saveObject(BMISample, withCompletion: { (success, error) - >Void in
- if success {
- print("添加成功") self.updateWeight()
- }
- if (error != nil) {
- print("添加失败")
- }
- })
- }
如果添加成功,你就可以去手机上的
查找 BMI,就可以看到我们刚才添加的 BMI 值,而且它的来源是我们创建的 app。
- 健康
创建一个
文件,并在 SB 中拖对应的 IB 文件,界面如下
- WorkOutsViewController.swift
然后在在
文件中添加请求 workout 的方法
- HeathManager.Swift
- /**
- 获取workoutData
- */
- func fetchWorkOutsData(completion: ([AnyObject] ! , NSError ! ) - >Void) {
- let workOutsSampleType = HKSampleType.workoutType() let workOutsPredicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(.Running) let sortDescrptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false) let workOutsQuery = HKSampleQuery(sampleType: workOutsSampleType, predicate: workOutsPredicate, limit: 0, sortDescriptors: [sortDescrptor]) { (workoutsQuery, results, error) - >Void in
- if (error != nil) {
- print("获取失败") return
- }
- if results != nil {
- completion(results ! , nil)
- }
- }
- self.hkHealthStore.executeQuery(workOutsQuery)
- }
然后在
文件的
- WorkOutsViewController.swift
方法中请求
- viewWillAppear()
- workout
- self.healthManager ? .fetchWorkOutsData({ (results, error) - >Void in
- if error != nil {
- print("获取失败")
- } else {
- self.workOuts = results as ! [HKWorkout]
- }
- dispatch_async(dispatch_get_main_queue(), { () - >Void in self.tableView.reloadData()
- });
- })
最后在 tableView 显示如下
在上面的界面的
的
- NavgationBar
向下再拖一个控制器,并添加对应的文件
- rightBarItem
,并在 IB 中设置界面信息如下
- AddWorkoutsViewController.swift
然后在
复写
- AddWorkoutsViewController.swift
, 针对点击不同的 cell,执行不同的方法。即让用户输入点击的 cell 对应的输入方式,比如时间就是时间选择器。距离就是一个警示框加一个文本框等。
- tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
- override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
- tableView.deselectRowAtIndexPath(indexPath, animated: true) self.tableView.tableFooterView = UIView() switch indexPath.row {
- case 0:
- self.setupPickerView()
- case 1,
- 2 : self.setupDatePickerView(indexPath.row)
- case 3,
- 4 : self.setupAlertView(indexPath.row)
- default:
- break
- }
- }
这里面对应的选择的方法就不一一介绍了,就是几个普通的 view 的添加。添加完所有的信息之后就可以点击
保存信息,方法如下:
- done
- @IBAction func addWorkOut(sender: AnyObject) {
- self.heathStore = HKHealthStore()
- //获取距离和能量的数值
- let distanceValue = Double(self.distanceLabel.text ! ) let energyValue = Double(self.energyLabel.text ! )
- //根据上面的数值创建对应的HKQuantity对象
- let distance = HKQuantity(unit: HKUnit.mileUnit(), doubleValue: distanceValue ! ) let energy = HKQuantity(unit: HKUnit.calorieUnit(), doubleValue: energyValue ! ) let endDate = self.dateFommater ? .dateFromString(self.endDateLabel.text ! ) let startDate = self.dateFommater ? .dateFromString(self.startDateLabel.text ! )
- //这里我默认设置成running了。可以根据具体的类型再进行设置。
- //创建HKWorkout对象。
- let workout = HKWorkout(activityType: .Running, startDate: startDate ! , endDate: endDate ! , workoutEvents: nil, totalEnergyBurned: energy, totalDistance: distance, metadata: nil)
- //保存上面创建的HKWorkout对象
- self.heathStore ? .saveObject(workout, withCompletion: { (success, error) - >Void in
- if error != nil {
- print("添加错误") return
- }
- if success {
- print("添加成功") dispatch_async(dispatch_get_main_queue(), { () - >Void in self.dismissViewControllerAnimated(true, completion: nil)
- })
- }
- })
- }
如果上面都执行成功,
就会模态消失,然后在上面的一个页面
- AddWorkoutsViewController.swift
就会在 tableView 的最上层显示出我们刚才添加成功的
- `WorkOutsViewController.swift
- HKWorkout
在本人过完春节回到公司上班之后经理问我
app 里面的信息能不能获取到。之前只是简单了解了这个框架,但是里面的具体结构体系并不了解。就趁着项目不忙,抽空把 HeathKit 学习了解了一下。本文的 demo 也采用了之前自学的 Swift 简单的实现了一下 (属于 Switer 新手)。可能会有错误或不准确的地方,如果你看到了,可以给我联系,我会及时更改的。写这篇文章一是对 HeathKit 的学习的一个练习,在这也是给以后会用到的童鞋一个可以参考的东西。
- 健康
HeathKit 不只是上面的这些内容,但是能把上面的这些问题搞定,我觉得针对 HeathKit 的体系会有一个清楚的认识,学习 HeathKit 更深层次的内容会有很大的帮助。
本文的已经放到 github 上面,需要的同学可以下载看看。
本文参考文章:
来源: http://lib.csdn.net/article/ios/44870