HealthKit 睡眠分析

隨著移動(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ù)的讀寫紫谷,因此需要配置NSHealthUpdateUsageDescriptionNSHealthShareUsageDescription兩個(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:

  1. 定義兩個(gè)NSDate對(duì)象掖鱼,分別代表開始時(shí)間和結(jié)束時(shí)間
  2. 使用HKCategoryTypeIdentifierSleepAnalysis創(chuàng)建HKObjectType實(shí)例
  3. 創(chuàng)建一個(gè)新的HKCategorySample對(duì)象實(shí)例然走,通常情況下使用類別樣本記錄睡眠數(shù)據(jù),個(gè)別的樣本數(shù)據(jù)正好代表了用戶是在床上還是睡著了戏挡,因此需要?jiǎng)?chuàng)建一個(gè)在床上的樣本對(duì)象(inBedSample)和睡著了的樣本對(duì)象(asleepSample)
  4. 最后芍瑞,使用HKHealthStoresaveObject方法,保存這些對(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ò)濾startDateendDate之間的數(shù)據(jù)屑那,可以通過(guò)HKQuerypredicateForSamplesWithStartDate來(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)用,可以在這里下載使用膜赃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挺邀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子跳座,更是在濱河造成了極大的恐慌端铛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疲眷,死亡現(xiàn)場(chǎng)離奇詭異禾蚕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狂丝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門换淆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人美侦,你說(shuō)我怎么就攤上這事产舞。” “怎么了菠剩?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)耻煤。 經(jīng)常有香客問(wèn)我具壮,道長(zhǎng),這世上最難降的妖魔是什么哈蝇? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任棺妓,我火速辦了婚禮,結(jié)果婚禮上炮赦,老公的妹妹穿的比我還像新娘怜跑。我一直安慰自己,他們只是感情好吠勘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布性芬。 她就那樣靜靜地躺著,像睡著了一般剧防。 火紅的嫁衣襯著肌膚如雪植锉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天峭拘,我揣著相機(jī)與錄音俊庇,去河邊找鬼狮暑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辉饱,可吹牛的內(nèi)容都是我干的搬男。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼彭沼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼止后!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起溜腐,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤译株,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后挺益,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歉糜,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年望众,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匪补。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烂翰,死狀恐怖夯缺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情甘耿,我是刑警寧澤踊兜,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站佳恬,受9級(jí)特大地震影響捏境,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毁葱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一垫言、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倾剿,春花似錦筷频、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至际度,卻和暖如春葵袭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乖菱。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工坡锡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蓬网,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓鹉勒,卻偏偏與公主長(zhǎng)得像帆锋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子禽额,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容