今天,睡眠革命已经成为一种时尚,并且人们比以往任何时候都想了解他们睡眠的时间,更想分析收集自己睡眠时间趋势透露出来的信息,先进的技术,硬件,手机把这个看似不断增长的项目带入了一个新的时代。
苹果提供了一种很酷并且安全的方式来与用户个人健康信息进行通信,并且通过内置的健康App对这些信息进行安全的存储。你不仅可以使用HealthKit建立一个健身的应用程序,同时这个框架也允许你访问睡眠质量等数据。
在本教程中,我会快速的向你介绍这个HealthKit框架,并且掩饰如何创建一个基于睡眠数据分析的简单应用。
Introduction---前言介绍
HealthKit框架提供了一个结构用于将数据以加密的数据库的方式保存在 HealthKit store
中,你可以使用HKHealthStore类访问此数据库, iPhone
和Apple Watch
都有他们自己的HealthKit store
,健康数据可以在iPhone
和Apple Watch
之间同步。然而为了节省空间,Apple Watch
上过早的数据会被周期性的清除。HealthKit框架和Health 应用并不能在iPad上使用。
如果你想创建一个基于健康数据的iOS或者watchOS应用,HealthKit将是一个强大的工具。它的设计目的就是在更广泛的资源上来管理数据,自动的合并基于用户偏好的不同来源的数据。应用程序可以访问各个资源的原始数据并且可以合并数据本身,不仅可以针对身体的测量,健身或营养情况,该数据也可以用于睡眠质量等分析。
在接下来的文章中,我将告诉你如何使用HealthKit framework在iOS上保存和访问睡眠分析数据,当然,方法同样适用于watchOS的应用。值得注意的是,本教程使用Swift 2.0 和 Xcode 7.编写,所以确保你的Xcode版本在7.0以上。
在开始之前,请下载启动项目并解压缩,我已经创建了一个基本功能的用户界面,当你运行启动这个项目,你会看到一个计时器,显示你按下按钮之后经过的时间。
Working with the HealthKit Framework---HealthKit框架的使用
我们应用程序的目的是利用Start
和 Stop
按钮来实现保存睡眠分析信息和取出数据,要使用HealthKit必须在一开始的时候授权你的应用程序访问HealthKit:打开你的Xcode->project->target ,打开HealthKit功能。如下图
接下来,需要创建一个HKHealthStore的实例,在你的ViewControlle类中粘贴如下代码:
let healthStore = HKHealthStore()```
稍后,我们会使用HKHealthStore实例来访问`HealthKit store`
正如之前提到的,HealthKit必须经过授权才可以访问并控制他们的健康数据,所以你必须先请求用户的许可才可以访问(读/写)用户的睡眠分析数据。要做到这一点,第一步首先到导入内置的HealthKit框架( HealthKit framework)并且更新你的`viewDidLoad`方法,代码如下:
override func viewDidLoad() {
super.viewDidLoad()
let typestoRead = Set([
HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis)!
])
let typestoShare = Set([
HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis)!
])
self.healthStore.requestAuthorizationToShareTypes(typestoShare, readTypes: typestoRead) { (success, error) -> Void in
if success == false {
NSLog(" Display not allowed")
}
}
}```
上面的代码会提示用户允许或者拒绝权限请求,在完成的block中可以对成功或失败出错进行处理,并且也可以得到最终的结果。当然用户并不是必须或者一定会同意你的应用程序所有的权限请求,所以你必须在程序中妥善的处理错误情况。
但是出于测试目的,你必须选择“允许”选项,授予应用程序权限访问设备上的健康数据。如下图:
Writing Sleep Analysis Data---写入睡眠分析数据
首先,我们如何才能获取睡眠分析数据呢?根据苹果的官方文档,每个睡眠分析样本只能有一个值,这个值代表用户无论躺在床上还是已经睡着了,HealthKit用两个或者多个重复时间的样本,通过比较这些样本的开始和结束时间,应用程序统计计算出来的一个值:
the amount of time it takes for the user to fall asleep
the percentage of time in bed that the user actually sleeps
the number of times the user wakes up while still in bed
the total amount of time spent both in bed and asleep
简单来说,按照下面的方法把分析得到的睡眠数据保存到HealthKit store
:
1.我们需要定义对应于开始时间和结束时间的两个`NSDate`对象。
2.然后我们使用`HKCategoryTypeIdentifierSleepAnalysis`创建一个`HKObjectType`实例。
3.我们需要创建一个`HKCategorySample`类型的新对象,通常使用样本的类别来记录睡眠数据。每个 样本代表用户躺在床上或者进入睡眠的时间段。所以我们会同时创建一个躺在床上的样本和一个进入睡眠状态的样本。
4.最后我们用`HKHealthStore`的`saveObject`方法来保存对象。
注意的是:对于样本的类型,可以查看HealthKit Constants Reference
链接中的内容是Objective-C版本,如果你要翻译成swift版本,这里是保存睡眠数据的代码,请添加到ViewController
类中:
func saveSleepAnalysis() {
// alarmTime and endTime are NSDate objects
if let sleepType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis) {
// we create our new object we want to push in Health app
let object = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.InBed.rawValue, startDate: self.alarmTime, endDate: self.endTime)
// at the end, we save it
healthStore.saveObject(object, withCompletion: { (success, error) -> Void in
if error != nil {
// something happened
return
}
if success {
print("My new data was saved in HealthKit")
} else {
// something happened again
}
})
let object2 = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.Asleep.rawValue, startDate: self.alarmTime, endDate: self.endTime)
healthStore.saveObject(object2, withCompletion: { (success, error) -> Void in
if error != nil {
// something happened
return
}
if success {
print("My new data (2) was saved in HealthKit")
} else {
// something happened again
}
})
}
}```
上述方法在我们想要把睡眠分析数据保存到`HealthKit`的时候调用。
###Reading Sleep Analysis Data---读取睡眠分析数据
要想获取睡眠分析数据,需要创建一个`query`(查询语句),首先定义一个`HKObjectType`类别`HKCategoryTypeIdentifierSleepAnalysis`,然后你还需要是用一个时间条件使用`startDate`和`endDate`(NSDate类型)来获取检索时间范围内的数据,你还需要创建一个`sortDescriptor`排序的检索查询以获取所期望的数据。
下面是检索睡眠分析数据的示例代码:
func retrieveSleepAnalysis() {
// first, we define the object type we want
if let sleepType = HKObjectType.categoryTypeForIdentifier(HKCategoryTypeIdentifierSleepAnalysis) {
// Use a sortDescriptor to get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// something happened
return
}
if let result = tmpResult {
// do something with my data
for item in result {
if let sample = item as? HKCategorySample {
let value = (sample.value == HKCategoryValueSleepAnalysis.InBed.rawValue) ? "InBed" : "Asleep"
print("Healthkit sleep: \(sample.startDate) \(sample.endDate) - value: \(value)")
}
}
}
}
// finally, we execute our query
healthStore.executeQuery(query)
}
}
用刚才的代码查询HealthKit获得所有的睡眠分析数据,然后降序排列,每个查询语句都打印了`startDate`,`endDate`,`value`,上述代码已经设置了上限30天来检索过去30天的样本,你也可以自定义方法来选择你自己认为的开始和结束时间。
###App Testing---测试
对于上面的Demo,我使用NSTimer来显示距离按下按钮的时间,保存这个按钮的开始和结束时间内(NSDate对象)的睡眠分析数据,在停止的方法里,可以调用`saveSleepAnalysis()`和`retrieveSleepAnalysis()`方法来保存和获取睡眠数据。
@IBAction func stop(sender: AnyObject) {
endTime = NSDate()
saveSleepAnalysis()
retrieveSleepAnalysis()
timer.invalidate()
}
在你的app里,你需要改变NSDate对象来选择相应的开始和结束时间(可能不相同)来保存躺在床上和进入睡眠状态的值。
一旦更改了之后,你就可以运行你的程序启动定时器,,让它运行几分钟之后按下停止按钮,打开Health app,你就会看到相应的睡眠数据。如下图:
![](http://upload-images.jianshu.io/upload_images/701353-6f7993c57e45bca8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###Some Advices for HealthKit Apps---总结与建议
HealthKit苹果设计这个框架的目的就是提供给开发者一个共享平台,用以分享或访问用户的数据,避免数据中可能的重复和不一致。苹果的审核说明明确指出,使用了HealthKit并要求读写权限的但是对于数据使用没有明确的示例和说明的话会被拒。
保存假的或者不正确的数据到Health App也会被拒(悲剧),意味着对于计算不同的健康值你的算法不能太low,或者存在侥幸心理。就像本教程中睡眠分析计算那样,你应该尝试使用内置的传感器数据读取和处理任何参数,以避免错误的计算数据。
有关完整的Xcode项目,[看这里](https://github.com/appcoda/SleepAnalysis).