隨著移動(dòng)手機(jī)設(shè)備硬件的進(jìn)步般堆,持續(xù)帶來(lái)了各種人性化的指標(biāo)分析體系篙议,例如運(yùn)動(dòng)數(shù)據(jù)統(tǒng)計(jì)分析唾糯、飲食習(xí)慣統(tǒng)計(jì)分析等等,大大增強(qiáng)了人類對(duì)于自身各種活動(dòng)的認(rèn)知和理解鬼贱。而在這個(gè)快節(jié)奏的時(shí)代移怯,睡眠質(zhì)量的分析比以往任何時(shí)候都更加的有意義,一場(chǎng)睡眠革命正在悄然的在人們的生活中崛起这难。通過(guò)針對(duì)睡眠質(zhì)量的統(tǒng)計(jì)分析舟误,能夠清晰的獲知人們睡眠的開始、結(jié)束姻乓、從而顯露出睡眠的趨勢(shì)等等嵌溢。
蘋果提供了一種非常酷的方式以用來(lái)于用戶的個(gè)人健康信息進(jìn)行數(shù)據(jù)交流蹋岩,并且保持了數(shù)據(jù)安全和存儲(chǔ)安全赖草,這就是蘋果在iOS系統(tǒng)中內(nèi)建的健康應(yīng)用 --- Health。而Health應(yīng)用程序使用的技術(shù)框架是蘋果專門打造的一款健康服務(wù)框架 --- HealthKit剪个,開發(fā)者不僅可以使用此框架構(gòu)建自身的健康類應(yīng)用程序秧骑,而且還允許訪問(wèn)健康應(yīng)用中的數(shù)據(jù)分析結(jié)果。
在Health應(yīng)用程序中扣囊,并沒(méi)有自動(dòng)去統(tǒng)計(jì)用戶的睡眠起始時(shí)間等數(shù)據(jù)乎折,但是Health為我們提供了一套數(shù)據(jù)寫入和分析功能,開發(fā)者可以依照特定的數(shù)據(jù)格式將睡眠數(shù)據(jù)寫入到Health侵歇,通過(guò)Health分析后骂澄,得到數(shù)據(jù)的分析結(jié)果。
簡(jiǎn)介
HealthKit 框架提供了一種保存數(shù)據(jù)的加密數(shù)據(jù)庫(kù)叫做Health Store
惕虑,可以使用HKHealthStore
類來(lái)訪問(wèn)此數(shù)據(jù)庫(kù)坟冲,而且不僅在iPhone還是Apple Watch上都有自己的Health Store
。健康數(shù)據(jù)可以在iPhone和Apple Watch之間進(jìn)行同步枷遂,不僅如此樱衷,Health Store
也會(huì)定期的清除老舊的數(shù)據(jù),以節(jié)省存儲(chǔ)空間酒唉。需要注意的是HealthKit和Health應(yīng)用程序均不支持iPad矩桂。
如果需要針對(duì)健康數(shù)據(jù)構(gòu)建一個(gè)iOS應(yīng)用或者watchOS應(yīng)用,HealthKit是首選的框架工具。HealthKit能夠統(tǒng)一管理來(lái)自各種數(shù)據(jù)源的數(shù)據(jù)侄榴,根據(jù)用戶的喜好自動(dòng)合并來(lái)自不同數(shù)據(jù)源的數(shù)據(jù)雹锣,并且能夠訪問(wèn)到每個(gè)數(shù)據(jù)來(lái)源平臺(tái)的數(shù)據(jù),并將數(shù)據(jù)合并癞蚕。例如多個(gè)睡眠分析的應(yīng)用程序均向健康應(yīng)用寫入了睡眠時(shí)間數(shù)據(jù)蕊爵,在健康應(yīng)用中,你可以看到健康應(yīng)用將多個(gè)時(shí)間進(jìn)行合并到了一起桦山,統(tǒng)一作為睡眠分析的數(shù)據(jù)來(lái)源攒射,并通過(guò)可視化進(jìn)行圖表展示,用戶還可以在健康應(yīng)用中選擇日恒水、周会放、月、年來(lái)查看合并后的睡眠情況钉凌。
這樣不僅可以對(duì)用戶的體征進(jìn)行測(cè)量記錄哎媚,運(yùn)動(dòng)健身統(tǒng)計(jì)或者飲食營(yíng)養(yǎng)數(shù)據(jù)統(tǒng)計(jì)脊岳,還可以用于針對(duì)睡眠數(shù)據(jù)進(jìn)行分析等蓝谨。
在接下來(lái)削樊,將使用Healthkit框架訪問(wèn)和保存用戶的睡眠數(shù)據(jù),了解用戶的睡眠情況酸纲,同樣的方法在watchOS上也是適用的捣鲸,工程樣例和代碼使用了Swift 3.0 和Xcode 8 進(jìn)行構(gòu)建。
在開始之前福青,可以下載 Starter project摄狱,此開始工程中已經(jīng)創(chuàng)建了用戶界面以及一些方法,當(dāng)運(yùn)行了此開始工程后无午,可以看到界面上有一個(gè)計(jì)時(shí)器數(shù)據(jù)展示標(biāo)簽媒役,按下開始按鈕后,計(jì)時(shí)器讀數(shù)將會(huì)持續(xù)變化宪迟。
使用HealthKit框架
我們的目標(biāo)應(yīng)用程序主要的功能是保存用戶的睡眠分析信息和使用開始
和停止
按鈕檢索數(shù)據(jù)酣衷。為了使用HealthKit,你必須給予應(yīng)用程序HealthKit功能程序包次泽,選擇工程當(dāng)前的target -> capabilities
穿仪,打開HealthKit功能程序包。
開啟此功能包以后意荤,根據(jù)iOS10最新的權(quán)限管理機(jī)制啊片,還需要在工程的info.plist文件中配置全新說(shuō)明,針對(duì)HealthKit來(lái)說(shuō)玖像,由于需要進(jìn)行數(shù)據(jù)的讀寫紫谷,因此需要配置NSHealthUpdateUsageDescription
和NSHealthShareUsageDescription
兩個(gè)字段。
接下來(lái),需要在ViewController
類中創(chuàng)建一個(gè)HKHealthStore
實(shí)例:
let healthStore = HKHealthStore()
之后將使用HKHealthStore
實(shí)例訪問(wèn)HealthKit數(shù)據(jù)庫(kù)笤昨。
如前所述祖驱,HealthKit需要用戶授權(quán)才能夠訪問(wèn)健康數(shù)據(jù),所以必須首先向用戶請(qǐng)求權(quán)限許可才能夠訪問(wèn)(讀/寫)睡眠分析數(shù)據(jù)瞒窒。因此在ViewController
中的viewDidLoad
方法中捺僻,進(jìn)行權(quán)限的申請(qǐng):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let typestoRead = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
let typestoShare = Set([
HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!
])
self.healthStore.requestAuthorization(toShare: typestoShare, read: typestoRead) { (success, error) -> Void in
if success == false {
NSLog(" Display not allowed")
}
}
}
這段代碼將彈出申請(qǐng)權(quán)限提示頁(yè),用戶可以允許和拒絕請(qǐng)求崇裁,在回調(diào)Block中匕坯,可以處理成功或者失敗的組中結(jié)果,當(dāng)用戶拒絕了權(quán)限的申請(qǐng)請(qǐng)求寇壳,會(huì)有錯(cuò)誤的信息提示醒颖。
但是為了測(cè)試方便妻怎,每次權(quán)限申請(qǐng)的時(shí)候壳炎,可以直接選擇允許
,允許訪問(wèn)設(shè)備上的健康數(shù)據(jù)逼侦。
寫睡眠分析數(shù)據(jù)
在寫數(shù)據(jù)之前匿辩,如何才能檢索睡眠分析數(shù)據(jù)呢?根據(jù)蘋果的官方文檔榛丢,每個(gè)睡眠分析樣本只能有一個(gè)值铲球。為了表示用戶是在床上
還是睡著了
還是醒著
,HealthKit使用兩個(gè)或兩個(gè)以上的重疊時(shí)期樣本晰赞。通過(guò)比較這些樣本的開始和結(jié)束時(shí)間稼病,應(yīng)用程序可以進(jìn)行二次計(jì)算進(jìn)行統(tǒng)計(jì)分析:
- 用戶入睡的時(shí)間
- 在床上的時(shí)間占比
- 用戶在床上醒來(lái)的次數(shù)
- 在床上和在床上睡著的時(shí)間總量
簡(jiǎn)而言之,可以按照以下的步驟來(lái)保存睡眠分析數(shù)據(jù)到HealthStore:
- 定義兩個(gè)
NSDate
對(duì)象掖鱼,分別代表開始時(shí)間和結(jié)束時(shí)間 - 使用
HKCategoryTypeIdentifierSleepAnalysis
創(chuàng)建HKObjectType
實(shí)例 - 創(chuàng)建一個(gè)新的
HKCategorySample
對(duì)象實(shí)例然走,通常情況下使用類別樣本記錄睡眠數(shù)據(jù),個(gè)別的樣本數(shù)據(jù)正好代表了用戶是在床上還是睡著了戏挡,因此需要?jiǎng)?chuàng)建一個(gè)在床上的樣本對(duì)象(inBedSample)和睡著了的樣本對(duì)象(asleepSample) - 最后芍瑞,使用
HKHealthStore
的saveObject
方法,保存這些對(duì)象即可
func saveSleepAnalysis() {
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// we create new object we want to push in Health app
let inBedSample = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.inBed.rawValue, start: self.alarmTime, end: self.endTime)
// we now push the object to HealthStore
healthStore.save(inBedSample, withCompletion: { (success, error) -> Void in
if error != nil {
// handle the error in your app gracefully
return
}
if success {
print("My new data inBedSample was saved in Healthkit")
} else {
// It was an error again
}
})
let asleepSample = HKCategorySample(type:sleepType, value: HKCategoryValueSleepAnalysis.asleep.rawValue, start: self.alarmTime, end: self.endTime)
healthStore.save(asleepSample, withCompletion: { (success, error) -> Void in
if error != nil {
// handle the error in your app gracefully
return
}
if success {
print("My new data asleepSample was saved in Healthkit")
} else {
// It was an error again
}
})
}
}
這個(gè)方法可以在希望保存睡眠分析數(shù)據(jù)的時(shí)候調(diào)用褐墅。
讀取睡眠分析數(shù)據(jù)
為了讀取或者叫檢索睡眠分析數(shù)據(jù)拆檬,首先需要?jiǎng)?chuàng)建一個(gè)查詢,在創(chuàng)建查詢之前妥凳,定義需要的對(duì)象類型竟贯,可以使用HKCategoryTypeIdentifierSleepAnalysis
中的HKObjectType
來(lái)定義∈旁浚或許還需要使用正則式來(lái)過(guò)濾startDate
和endDate
之間的數(shù)據(jù)屑那,可以通過(guò)HKQuery
的predicateForSamplesWithStartDate
來(lái)創(chuàng)建一個(gè)正則式。為了使得數(shù)據(jù)能夠帶有順序,比如按照時(shí)間升序或者降序齐莲,可以創(chuàng)建一個(gè)sortDescriptor
來(lái)排序數(shù)據(jù)痢站。
整個(gè)檢索數(shù)據(jù)的代碼段如下:
func retrieveSleepAnalysis() {
// startDate and endDate are NSDate objects
// ...
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// You may want to use a predicate to filter the data... startDate and endDate are NSDate objects corresponding to the time range that you want to retrieve
//let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// Get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// the block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 30, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// Handle the error in your app gracefully
return
}
if let result = tmpResult {
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)")
}
}
}
}
healthStore.execute(query)
}
}
這段代碼查詢了健康應(yīng)用中的說(shuō)有數(shù)據(jù),然后進(jìn)行了降序排列选酗。每次查詢都打印出了樣本的開始時(shí)間和結(jié)束時(shí)間阵难,以及在這個(gè)時(shí)間段內(nèi)用戶的睡眠狀態(tài),例如芒填,在床上或者睡眠呜叫。代碼中限制了數(shù)據(jù)條目的數(shù)量30條,這樣查詢會(huì)只檢索30條數(shù)據(jù)殿衰,然后返回朱庆,也可以使用正則式來(lái)進(jìn)行指定時(shí)間段數(shù)據(jù)的檢索等。
應(yīng)用測(cè)試
對(duì)于Demo應(yīng)用程序來(lái)說(shuō)闷祥,已經(jīng)設(shè)定了一個(gè)NSTimer
來(lái)對(duì)睡眠情況的計(jì)時(shí)娱颊。NSDate
對(duì)象會(huì)在用戶點(diǎn)擊開始按鈕后,記錄下用戶的睡眠開始時(shí)間點(diǎn)凯砍,點(diǎn)擊停止按鈕后箱硕,記錄下睡眠結(jié)束的時(shí)間點(diǎn),并最終用于保存睡眠數(shù)據(jù)的一部分悟衩。在結(jié)束按鈕點(diǎn)擊時(shí)剧罩,需要將記錄時(shí)間段的數(shù)據(jù)保存,同時(shí)檢索更新后的數(shù)據(jù)座泳,用戶界面展示惠昔。
@IBAction func stop(_ sender: AnyObject) {
endTime = Date()
self.saveSleepAnalysis()
self.retrieveSleepAnalysis()
timer.invalidate()
}
此時(shí)運(yùn)行工程,點(diǎn)擊開始按鈕挑势,等待一段時(shí)間后镇防,點(diǎn)擊停止按鈕,完整后薛耻,打開健康應(yīng)用营罢,進(jìn)入睡眠分析,就可以看到相關(guān)的數(shù)據(jù)了饼齿。
HealthKit應(yīng)用的一些建議
HealthKit旨在提供一個(gè)公共的平臺(tái)饲漾,應(yīng)用程序開發(fā)人員能夠很容易地共享和訪問(wèn)用戶的數(shù)據(jù),并且避免了可能的重復(fù)或者不一致的數(shù)據(jù)缕溉。蘋果審核指南非常具體的指明了使用HealthKit和請(qǐng)求讀/寫數(shù)據(jù)的權(quán)限的要求考传,如果不滿足這些要求,應(yīng)用程序會(huì)被拒絕上架证鸥。并且針對(duì)用戶數(shù)據(jù)的使用不當(dāng)僚楞,也被會(huì)拒絕。這就意味著泉褐,不能夠?qū)懭氩缓弦?guī)或者不合理的數(shù)據(jù)赐写。
獲取完整的Demo應(yīng)用,可以在這里下載使用膜赃。