如今璧瞬,睡眠革命逐漸成為一種時(shí)尚,跟之前相比渐夸,人們不僅想知道自己的睡眠時(shí)間嗤锉,同時(shí)對(duì)用于分析他們一定時(shí)間段內(nèi)睡眠狀態(tài)的數(shù)據(jù)感興趣。包括硬件技術(shù)在內(nèi)的墓塌,尤其是手機(jī)技能的提升瘟忱,為用戶這一日益增長(zhǎng)的需求帶來了全新的希望.
蘋果提供了一種安全奥额、炫酷的方式來顯示用戶的個(gè)人健康信息,并將這些信息安全的存儲(chǔ)在 Health 這個(gè)內(nèi)置的應(yīng)用中访诱。你不僅可以使用 HealthKit 來 創(chuàng)建一個(gè)健身應(yīng)用 垫挨,該框架還允許你訪問睡眠的詳細(xì)數(shù)據(jù)。這本次教程中触菜,我將會(huì)給大家簡(jiǎn)要的介紹 HealthKit 這個(gè)框架九榔,并演示如何創(chuàng)建一個(gè)簡(jiǎn)單的睡眠分析的應(yīng)用。
**簡(jiǎn)介 **
HealthKit 提供了一個(gè)名為 HealthKit store 的結(jié)構(gòu)用來存儲(chǔ)加密的數(shù)據(jù)涡相。你可以使用HKHealthStore
這個(gè)類來訪問這個(gè)數(shù)據(jù)庫(kù)哲泊。iPhone 和 Apple Watch 都有自己的 HealthKit store,他們之間的健康數(shù)據(jù)是同步催蝗。需要注意的是切威,在 Apple Watch 上老的數(shù)據(jù)會(huì)被定期的清除以節(jié)省空間。并且 丙号,iPad不支持HealthKit 和 Health 先朦。
如果你想基于健康數(shù)據(jù)創(chuàng)建一個(gè) iOS 或者 watchOS 應(yīng)用,HealthKit 是一個(gè)非常強(qiáng)大的工具犬缨。它被設(shè)計(jì)的目的就是管理廣泛地?cái)?shù)據(jù)源烙无,并且根據(jù)用戶的偏好自動(dòng)地合并不同來源的數(shù)據(jù)。應(yīng)用程序還可以訪問每一條原始數(shù)據(jù)遍尺,并且合并他們截酷。這些數(shù)據(jù)不僅能夠用于身體評(píng)估,健康狀況評(píng)估以及營(yíng)養(yǎng)狀況評(píng)估乾戏,還能夠用于分析睡眠質(zhì)量迂苛。
接下來,我將會(huì)向你展示在 iOS 上如何利用 HealthKit 保存和訪問睡眠數(shù)據(jù)鼓择。這種方法也同樣適合 watchOS 上的應(yīng)用三幻。請(qǐng)注意本次教程使用的是 Swift 2.0 和 Xcode 7。因此呐能,請(qǐng)確保你跟隨此教程的時(shí)候使用的是 Xcode 7(或者以上)念搬。
在此之前,下載最開始的工程 并解壓摆出。我已經(jīng)為你創(chuàng)建了基本功能的用戶界面朗徊。當(dāng)你運(yùn)行這個(gè)工程的時(shí)候,你將會(huì)看到一個(gè)定時(shí)器的 UI 偎漫,當(dāng)你按下開始按鈕以后就會(huì)顯示計(jì)時(shí)爷恳。
** 使用 HealthKit**
我們使用這個(gè)應(yīng)用的目的是使用開始和停止按鈕來保存睡眠分析信息和復(fù)取數(shù)據(jù)。要使用 HealthKit象踊,你必須給你的應(yīng)用授權(quán)温亲。也就是在你的應(yīng)用中棚壁,按著 target -> capabilities -> 打開HealthKit開關(guān)
這個(gè)步驟
接下來,你需要用下面的代碼在 ViewController
類中創(chuàng)建一個(gè) HKHealthStore
的實(shí)例:
let healthStore = HKHealthStore()
隨后我們將使用 HKHealthStore
這個(gè)實(shí)例來訪問 HealthKit store栈虚。
如之前所說袖外, HealthKit 授予用戶權(quán)限來訪問他們的健康數(shù)據(jù)。所以你在獲得用戶的睡眠分析數(shù)據(jù)權(quán)限(讀/寫)之前魂务,你必須現(xiàn)請(qǐng)求用戶的許可在刺。要做到這一點(diǎn),首先要導(dǎo)入內(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")
}
}
}
這段代碼將提示用戶允許或拒絕請(qǐng)求的訪問的權(quán)限。在閉包中相艇,你可以對(duì)成功或者失敗進(jìn)行處理颖杏,并得到最終結(jié)果。用戶沒有必要授予你的應(yīng)用所有你請(qǐng)求的權(quán)限坛芽。所以在你的應(yīng)用中你必須妥善的處理這些錯(cuò)誤留储。
但是出于測(cè)試的目的,你必須選擇“允許”選項(xiàng)來授權(quán)你的應(yīng)用能訪問你設(shè)備上的健康數(shù)據(jù)咙轩。
寫入睡眠分析數(shù)據(jù)
首先获讳,我們?nèi)绾螐?fù)取睡眠分析數(shù)據(jù)呢?根據(jù)蘋果文檔活喊,每個(gè)睡眠分析樣品只能有一個(gè)值丐膝。為了區(qū)分用戶是在床上還是睡著了,HealthKit 用了兩個(gè)或者兩個(gè)以上的重疊時(shí)間的樣本钾菊。通過比較這些樣品的開始和結(jié)束時(shí)間帅矗,應(yīng)用就可以計(jì)算出一些次要的統(tǒng)計(jì)數(shù)據(jù):
用戶進(jìn)入睡眠所花費(fèi)的時(shí)間
用戶在床上的時(shí)間和實(shí)際睡眠時(shí)間的百分比
用戶在床上醒來的次數(shù)
用戶在床上和睡著的時(shí)間的總和
簡(jiǎn)單地說,你按照下面的方法來保存你的睡眠分析數(shù)據(jù)到 HealthKit 商店:
1煞烫、我們需要定義兩個(gè)
NSDate
的對(duì)象用于開始時(shí)間和結(jié)束時(shí)間浑此。2、用
HKCategoryTypeIdentifierSleepAnalysis
創(chuàng)建一個(gè)HKObjectType
的實(shí)例滞详。3凛俱、我們需要?jiǎng)?chuàng)建一個(gè)新的
HKCategorySample
類型的對(duì)象,你通常使用一類的樣本來記錄睡眠時(shí)間料饥,單個(gè)的樣本代表用戶在床上或者在睡覺的時(shí)間段蒲犬。因此,我們將會(huì)創(chuàng)建一個(gè)在床上和一個(gè)睡著了的重疊時(shí)間樣本稀火。4暖哨、最后赌朋,我們用
HKHealthStore
的saveObject
方法來保存上述實(shí)例凰狞。
如果你想用 Swift
實(shí)現(xiàn)上述所有篇裁,這里有保存在床上和睡眠中睡眠分析數(shù)據(jù)的代碼片段。請(qǐng)把這些代碼插入在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
}
})
}
}
當(dāng)我們想把睡眠分析數(shù)據(jù)保存到 HealthKit 的時(shí)候上述方法將會(huì)被調(diào)用赡若。
** 讀取睡眠分析數(shù)據(jù)**
要讀取睡眠分析數(shù)據(jù)达布,我們需要?jiǎng)?chuàng)建一個(gè)query
。首先你需要用 HKCategoryTypeIdentifierSleepAnalysis
定義一個(gè) HKObjectType
類型的實(shí)例逾冬,你可能還需要一個(gè)謂詞來篩選你用開始時(shí)間和結(jié)束時(shí)間復(fù)取的數(shù)據(jù)黍聂,也就是你想要檢索的時(shí)間范圍內(nèi)的 NSDate 類型的對(duì)象。你還需要?jiǎng)?chuàng)建一個(gè) sortDescriptor
排序的檢索詞來篩選出你想要的結(jié)果身腻。
你的檢索睡眠分析數(shù)據(jù)的代碼應(yīng)改像下面這樣:
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 上的所有睡眠分析數(shù)據(jù)并按降序排列产还。然后每天結(jié)果上都帶著是在床上的開始結(jié)束時(shí)間或者在睡覺的開始結(jié)束時(shí)間被打印出來。我已經(jīng)設(shè)置了上限30用來檢索最后記錄的30個(gè)樣本嘀趟。你也可以用謂詞的方法來選擇你的自定義開始時(shí)間和結(jié)束時(shí)間脐区。
應(yīng)用測(cè)試
對(duì)于這個(gè) demo,我已經(jīng)使用 NSTimer 來顯示你按下按鈕之后經(jīng)過的時(shí)間她按。當(dāng)開始和結(jié)束按鈕按下的時(shí)候?qū)?huì)創(chuàng)建 NSDate 的對(duì)象來保存所經(jīng)過的時(shí)間作為睡眠分析數(shù)據(jù)牛隅。在停止的方法中,你可以調(diào)用 saveSleepAnalysis()
和 retrieveSleepAnalysis
方法來保存和獲得睡眠數(shù)據(jù)酌泰。
@IBAction func stop(sender: AnyObject) {
endTime = NSDate()
saveSleepAnalysis()
retrieveSleepAnalysis()
timer.invalidate()
}
在你的應(yīng)用程序中例驹,你也許想要改變 NSDate 對(duì)象來選擇相關(guān)的開始和結(jié)束時(shí)間(可能不同)來保存在床上和睡眠的的值均函。
一旦你做了這些改變,你可以運(yùn)行這個(gè) demo 然后開始計(jì)時(shí)。讓它運(yùn)行幾分鐘圆丹,然后點(diǎn)擊停止按鈕。然后裂允,打開 Health 這個(gè)應(yīng)用廓旬,你就可以找到你的睡眠數(shù)據(jù)。
一些關(guān)于 HealthKit 應(yīng)用的建議
HealthKit 旨在提供一個(gè)公共平臺(tái)碘耳,使應(yīng)用程序開發(fā)人員能夠很容易地共享和訪問用戶的數(shù)據(jù)显设,并避免任何可能的重復(fù)或不一致的數(shù)據(jù)。蘋果審核指南對(duì)使用 HealthKit 有很明確的要求辛辨,如果對(duì)用戶的讀/寫權(quán)限請(qǐng)求沒有明確的描述就很可能導(dǎo)致應(yīng)用程序被拒絕捕捂。
應(yīng)用如果保存?zhèn)卧旎蛘咤e(cuò)誤的數(shù)據(jù)到健康應(yīng)用的話也將被拒絕。這就意味著斗搞,你不能像在本次教程中睡眠分析這樣天真的以你的算法計(jì)算不同的健康值指攒。你應(yīng)該嘗試使用內(nèi)置的傳感器數(shù)據(jù)讀取和操作一些參數(shù),以避免計(jì)算錯(cuò)誤。
關(guān)于完整的 Xcode 項(xiàng)目僻焚,你可以在 這里 看到允悦。