如何使用Swift來學習HealthKit中的sleep analysis
如今,有關睡眠的問題變得流行起來,用戶不僅對他們什么時候入睡感興趣,更關心的是一段時間之后,應用根據(jù)收集的數(shù)據(jù)所得出的結果。隨著硬件和手機技術的發(fā)展,有關科技與睡眠的話題變的極其受關注。
蘋果通過內(nèi)建的Health應用,提供了一種簡單酷炫的方式來安全的訪問和存儲用戶的健康信息。你不僅可以用HealthKit來開發(fā)一個健康類應用,還可以利用framework來獲取有關sleep analysis的數(shù)據(jù)。
在這個教程里,我將帶你快速了解HealthKit的框架,并且演示如何開發(fā)一個簡單的sleep analysis應用。
簡介
HealthKit框架提供了一種結構HealthKit store,利用它你可以把信息存儲在一個加密的數(shù)據(jù)庫里。你可以通過HKHealthStore這個類來訪問數(shù)據(jù)庫。iPhone和Apple Watch分別擁有自己的HealthKit store,并且里面的數(shù)據(jù)在iPhone和Apple Watch之間是同步的。但是有一點需要注意,在Apple Watch中,為了節(jié)省空間,舊的數(shù)據(jù)是會被清除的。而iPad是不支持Health Kit和Health應用的。
你如果想開發(fā)一個iOS或者watchOS的應用,那么HealthKit是一個強大的工具。HealthKit就是被設計用來管理來自不同資源的數(shù)據(jù),而且可以根據(jù)用戶的設置來合并這些數(shù)據(jù)。應用還可以訪問原始數(shù)據(jù),并且合并他們。身體指標,健康指標以及營養(yǎng)指標,這些數(shù)據(jù)都可以被用來做sleep analysis。
接下來,你將會了解如何使用HealthKit框架去保存和訪問iOS中的sleep analysis數(shù)據(jù)。以下的內(nèi)容同樣適用于watchOS。這個教程是基于Swift 2.0 和 Xcode 7. 請確保你是用Xcode 7或者以上的版本。
在這之前,下載初始工程文件并且解壓縮。我已經(jīng)為你創(chuàng)建了基本的界面。當你運行這個工程的時候,你會看到一個計時器的界面,點擊開始按鈕,計時器開始運行。
學習HealthKit框架
這個教程的目標是開發(fā)一個簡單的應用,可以存儲sleep analysis的數(shù)據(jù),以及通過開始 & 停止按鈕來獲取數(shù)據(jù)。第一步,你必須獲得HealthKit的許可。在你的工程文件中,選擇Current Target選項,選擇capabilities,并且打開對應的HealthKit。

下一步,你需要在ViewController中創(chuàng)建一個HKHealthStore實例,下面是代碼:
let healthStore = HKHealthStore()
接著,我們使用創(chuàng)建的HKHealthStore實例來訪問HealthKit store。
就像剛才提到的一樣,HealthKit允許用戶去選擇是否其他程序可以獲取他們的健康數(shù)據(jù)。所以在你訪問(讀/寫)sleep analysis數(shù)據(jù)之前,你需要獲取用戶的許可。怎么做呢?第一步,導入內(nèi)建的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")
}
}
}
以上的代碼將會請求獲取用戶的HealthKit權限,用戶可以選擇允許或者拒絕。在完成閉包中,你可以做更多的處理,例如獲取權限成功怎么辦,出現(xiàn)錯誤應該怎么辦。用戶有可能拒絕你的訪問權限,所以你必須要在完成閉包中小心的處理這些特殊情況。
為了教程的順利進行,你必須要選擇允許選項來使應用獲得你手機上的健康數(shù)據(jù)。

寫入sleep analysis數(shù)據(jù)
首先,我們?nèi)绾蔚玫絪leep analysis的數(shù)據(jù)呢?根據(jù)蘋果的官方文檔,每一個sleep analysis的樣本只包含一個值。這個值表示了用戶在床上的時間和睡眠的時間,HealthKit用了二個或者二個以上的樣本,每一個樣本都包含與其它樣本重疊的時間。通過這些樣本的開始時間和結束時間,我們能夠計算出這樣的一些數(shù)據(jù):
- 用戶入睡用了多長時間
- 用戶睡眠時間與用戶在床上時間的比例
- 用戶醒來后直到下床的時間
- 用戶在床上和用戶睡眠的總共時間

簡單來說,根據(jù)以下方法,我們能夠把sleep analysis數(shù)據(jù)存入到HealKit store中。
- 首先根據(jù)開始時間和結束時間,定義兩個
NSDate對象 - 利用
HKCategoryTypeIdentifierSleepAnalysis創(chuàng)建HKObjectType實例 - 創(chuàng)建一個
HKCategorySample實例。每一個樣本代表了用戶在床上或者睡眠的時間長度。所以我們創(chuàng)建一個在床上的樣本和睡眠的樣本,這兩個樣本有著沖重疊的時間。
4.最后,我們用HKHealthStore的saveObject方法來保存這個對象。
如果把上述的4步轉化為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
}
})
}
}
當想要存儲sleep analysis數(shù)據(jù)到HealthKit的時候,我們可以調(diào)用這個方法。
讀取sleep analysis數(shù)據(jù)
為了能夠讀取sleep analysis的數(shù)據(jù),我們需要執(zhí)行一個查詢操作。首先,定義一個HKObjectType類別,指明類別是HKCategoryTypeIdentifierSleepAnalysis。接下來可以用predicate去過濾得到的數(shù)據(jù),可以根據(jù)startDate和endDate這兩個NSDate類來得到你想要的時間區(qū)間。最后還創(chuàng)建一個sortDescriptor來根據(jù)你想得到的結果進行進一步的排序操作。
下面是以上過程的代碼:
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,并且得到所有的sleep analysis數(shù)據(jù),并且把這些數(shù)據(jù)按照降序排序。接下來每一個查詢都以startDate和endDate的形式被打印出來。例如In Bed 或者是 Asleep。 在創(chuàng)建query對象時,我在limit參數(shù)里傳入了30,表示我想得到最后30條記錄的樣本。當然了,你可以用在predicate參數(shù)中,傳入你自己定義的predicate對象,這樣你就可以自定義startDate和endDate了。
應用測試
在這個Demo應用中,我用了一個NSTimer來模擬真實的時間,當你按下開始按鈕后,計時器會運行。兩個NSDate對象在開始和結束按鈕被按下時分別被創(chuàng)建,這樣我們就可以模擬記錄sleep analysis數(shù)據(jù)了。在stop方法中,你可以調(diào)用saveSleepAnalysis()和retrieveSleepAnalysis()方法來存儲和讀取睡眠數(shù)據(jù)。
@IBAction func stop(sender: AnyObject) {
endTime = NSDate()
saveSleepAnalysis()
retrieveSleepAnalysis()
timer.invalidate()
}
在你的工程文件中,你不用完全遵循我的代碼,你也可以自由的改變startDate和endDate這兩個NSDate對象去存儲用戶在床上的時間和睡眠時間。
好了,現(xiàn)在你可以運行你的程序,開始計時。讓我們等上幾分鐘,然后點擊停止按鈕。之后,我們可以打開自帶的Health應用。你會發(fā)現(xiàn)睡眠數(shù)據(jù)已經(jīng)出現(xiàn)在界面中了。

小貼士時間
HealthKit是為開發(fā)者用來提供一個通用的平臺,通過這個平臺,我們可以輕松的共享,獲取用戶數(shù)據(jù),同時并且避免了重復或者前后矛盾的數(shù)據(jù)。在Apple審核應用規(guī)則中明確規(guī)定了,必須要清楚明確的請求獲取用戶的Health權限,如果不這么做,很可能你的應用會被拒絕上架。
如果一個應用試圖在Health應用中存入虛假的或者不正確的數(shù)據(jù),同樣會被拒絕上架。這意味著,像本教程中使用非常簡單的算法去計算不同的健康數(shù)據(jù),這樣也是不行的。你應該利用內(nèi)建的傳感器去獲取數(shù)據(jù),計算數(shù)據(jù),這樣,你獲得的數(shù)據(jù)會更加真實有效。
如果你想獲得完全的Xcode工程,你可以從這里獲取。