本文由海之号角(OceanHorn)翻译自Using Sleep Analysis in HealthKit with Swift
当今睡眠革命是一种时尚,用户比以往更好奇,不仅限于他们已经睡了多长时间,也关心通过收集一段时间的数据后分析展现出的睡眠趋势。包括硬件,尤其是手机的技术进步已经为这个日益增长的主题带来了新的曙光。
Apple 通过嵌入 Health 应用提供了一种非常酷的方式来安全的传输和存储用户的健康信息。你不光可以使用 HealthKit 搭建一款健身应用,也可以通过这个框架来访问睡眠统计数据。
在本教程中我将向你简单介绍 HealthKit 框架,并向你展示怎样搭建一款简单的睡眠汇总应用。
前言
HealthKit 框架通过一个加密数据库提供了用于存储数据的架构,称为 HealthKit store 。你可以通过使用 HKHealthStore
类访问这个数据库。iPhone 和 Apple Watch 有各自的 HealthKit store。健康数据在 Apple Watch 和 iPhone 间同步。但是 Apple Watch 中的过期数据会被周期性的清除以便节省存储空间。 HealthKit 框架和 Health 应用在 iPad 上不可用。
如果你想基于健康数据创建一款 iOS 或 watchOS 应用, HealthKit 是强有力的工具。 HealthKit 被设计用来管理来自广泛来源的数据,并且会基于用户设置自动的合并来自不同来源的数据。应用也可以访问每一个数据源的原始数据,然后自己合并。不只是身体测量、健身数据或者营养摄入,数据也可以被用在睡眠状况中。
本文的剩余部分我将向你展示怎么使用 HealthKit 框架在 iOS 中保存和访问睡眠汇总数据。同样的方法在 watchOS 应用中也适用。 请注意本教程的环境是 Swift2.0 和 Xcode7 。所以请确保使用 Xcode7 或更新版本来学习本教程。( 译者注: Swift2.2 及 Xcode7.3.1 可以运行通过。 )
在开始之前,请下载并解压初始项目。我已经为你实现了 UI 及基本的功能。当你运行初始项目时,在按开始按钮之后将会看到一个显示了已经开始多久的计时器 UI 。
HealthKit 框架的使用
我们这款应用的功能是通过 Start
和 Stop
按钮保存睡眠数据以及检索数据。要使用 HealthKit 框架,必须首先保证你的包中包含了 HealthKit 。在工程中找到 Targets -> Capabilities ,打开 HealthKit 的开关。
然后,你需要在 ViewController
类中通过下面的代码创建一个 HKHealthStore
的实例:
let healthStore = HKHealthStore()
之后我们将使用 HKHealthStore
实例来访问 HealthKit store中的数据。
像之前提到的那样,HealthKit 保证了用户能够控制自己的健康数据。所以你必须在访问(读/写)用户睡眠数据前首先获得用户的允许。为了实现这个目的,首先导入 HealthKit
框架,然后像下面这样更新 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")
}
}
}
上面的代码将提示用户允许或拒绝权限请求。在完成回调中,你可以处理成功或错误信息,并获得最终结果。当用户赋予应用所有权限时这段代码不是必须的。你必须在你的应用中优雅的处理这些错误。
但是对于测试目的,你必须选择 “Allow” 来保证应用能够访问到你设备上的健康数据。
写入睡眠汇总数据
首先,我们怎样才能读取到睡眠汇总数据? 根据苹果文档,每次睡眠统计采样数据都只能有一个值。为了表示用户在床休息和睡眠状态,HealthKit 使用了两个或更多的时间轴上重叠的采样数据。通过比较这些采样数据的开始和结束时间,第三方应用可以计算出一系列的二次统计数据。
-
用户进入睡眠所经过的时间
-
睡眠时间占在床休息时间的百分比
-
用户睡醒后依然在床休息的时间
-
在床休息与睡眠的总时间
简而言之,你可以按照下面的方法来保存睡眠汇总数据到 HealthKit store 中:
-
定义两个
NSDate
对象来对应开始时间与结束时间。 -
使用
HKCategoryTypeIdentifierSleepAnalysis
来创建一个HKObjectType
实例。 -
创建
HKCategorySample
类型的对象。一般的可以使用分类采样来记录睡眠数据。单独的采样数据分别来代表用户在床休息与睡眠的时间区间。我们将创建在时间轴上重叠的在床休息采样与睡眠时间采样。 -
最后,我们使用
HKHealthStore
的saveObject
来保存对象。
编辑注:对于采样的类型,可以查阅 HealthKit Constants Reference。
如果你将上面转化为 Swift 语言,就会得到下面保存在床休息与睡眠时间的睡眠汇总数据的代码片段。请将下面的方法插入 ViewControllerl
类中:
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 时调用。
读取睡眠汇总数据
要读取睡眠汇总数据,我们需要创建一个查询。首先需要通过定义 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 中所有的睡眠汇总数据然后按照降序排列。然后将每个查询到的结果的开始时间、结束时间以及类型(在床休息或睡眠时间)打印输出。代码中我限制为30个数据,将展示最后30条记录。你也可以使用谓词方法来自定义开始和结束时间。
应用测试
在demo应用中我使用了一个计时器来显示点击开始按钮后的时间。在开始和结束按钮按下后分别创建一个 NSDate
对象来计算时间长度作为睡眠汇总数据。在 stop
方法中调用 saveSleepAnalysis()
和 retrieveSleepAnalysis()
方法来保存和获取睡眠数据。
在你的应用中,你或许会想改变NSDate对象来选择相关的开始和结束时间(很可能不同)来保存在床休息时间和睡眠时间。
一旦改变了这些,你可以打开demo应用然后开始计时器。运行一段时间后点击结束按钮。在这之后打开 Health 应用,你将会看到记录的睡眠数据。
关于HealthKit应用的一些建议
HealthKit被设计用来作为应用开发者分享和方便访问用户数据,并且避免任何可能的数据重复或不一致的通用平台。苹果审核指南特别针对使用HealthKit及请求读写权限而没有明确说明使用目的的应用将导致应用被拒绝。
向Health应用中保存虚假数据的应用也将会被拒绝。这意味着你计算不同的健康数据的算法不能像本指南中的睡眠汇总这样幼稚。你应该使用嵌入的传感器数据并调整可能的参数来避免计算出错误数据。
你可以从这里获取到完整的Xcode项目。
网友评论