你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS中 HeathKit框架學習 步數統計等 韓俊強的博客

iOS中 HeathKit框架學習 步數統計等 韓俊強的博客

編輯:IOS開發綜合

HeathKit框架學習

本文結構
簡介用戶數據安全及隱私HeathKit框架HeathKit使用總結

簡介

HeathKit是Apple公司在推出iOS 8 系統時一塊推出的關於健康信息的框架。如果iPhone手機系統升級到iOS8之後就會發現多了一個健康-app,這就是Apple提供的一個記錄用戶健康信息的app,可以用它來分享健康和健身數據。還可以指定數據的來源,比如我們自己創建一個app,在我們的app中使用了HeathKit框架之後只要經過用戶的認證,就可以在我們的app之中給健康分享數據或者從健康中獲取數據。

HeathKit可以與健身設備一起工作,iPhone手機自身可以監控步數信息,會自動導入步數信息。但是其他信息或者設備需要配套的應該才能獲取到數據並導入到HeathKit中並在健康中顯示。

HeathKit不能再iPad中使用,而且它也不支持擴展。

用戶數據安全及隱私

由於用戶的健康信息可能是敏感的,所以這些用戶信息不能讓開發者很隨便的獲取到。每條信息的讀寫都需要用戶去選擇是否同意,比如用戶可以同意你獲取到用戶的身高體重,但是不同意讀寫生殖健康等其他用戶不願意公開的信息。為了防止信息洩露,我們是不知道用戶是否禁止了某條信息是否被用戶禁止讀取的。簡單的說,如果獲取不到某條信息,就代表沒有這條信息。

關於更多的關於隱私的信息,可以參考隱私

HeathKit框架

HeathKit在各個應用之間提供了一種有意義的方式共享數據。因此,我們必須使用HeathKit框架提供的數據類型和單位。這保證了數據存在的真正意義,我們不能自定義數據類型及單位。框架使用了子類化,例如HKObject和HKObjectType抽象類擁有很多有平行關系的子類,當使用Object或者ObjectType的時候,必須確保使用正確的子類。

在HeathKit中能夠存儲的類都是HKObject的子類,大部分HKObject的子類都是不可變的。每個對象都有下面的屬性:

UUID:每個對象的標識符Source:數據的來源,來源可以是HeathKit的健康app,也可以是我們自己創建的app。當一個對象存儲到HeathKit中時會設置其來源。只有從HeathKit中獲取到的數據的來源才有效。Metadata:一個包含該對象額外信息的字典,元數據包含預定義的key和自定義的key,預定義的key用來幫助我們在應用間共享數據,而自定義的key用來擴展HeathKit,為對象添加針對應用的數據。

HeathKit的對象主要分為特征和樣本。特征對象代表用戶的基本不變的數據,包括用戶的生日、血型和性別等。我們創建的app不能修改這些信息,只能讓用戶在健康中去修改或者添加個人特征信息。
樣本對象代表某個特定時間的數據,所有的樣本類型的對象都是HKSample的子類。它們都有下面的特性:

Type :樣本類型,例如:睡眠分享、步行距離、心率樣本等StartDate:樣本開始時間EndDate:樣本結束時間。如果是某一個時間的樣本,則開始於結束時間相同,如果是某個時間段的樣本,則結束時間在開始時間的後面。

樣本類型又可以分為四個類型:

類別樣本(HKCategorySample):在iOS 8 中,只有睡眠分析這一個類別樣本。代表有限種類的樣本.數量樣本(HKQuantitySample):這種樣本代表存儲數據的樣本,比如步數、距離、用戶的體溫等。它是HeathKit中最常見的數據類型。關系樣本(HKCorrelation):代表復合數據,包括一個或者多個樣本。在iOS 8 中,用correlation代表食物和血壓。在創建食物或血壓時,需要用correlation。訓練活動(HKWorkout):代表某種活動,比如走、跑步等。包含有開始時間、結束時間、運動類型、消耗能量、運動距離等屬性。還可以為workout關聯許多詳細的樣本。不像correlation,這些樣本不包含在workout中,但是可以通過workout獲取到。

再介紹一個HeathKit中經常用到的一些類。

HKSamle

每個HkSample的子類都有對應的便利方法創建對應的對象。比如:

對於數量樣本,需要創建HKQuantity類的實例。而且數量的單位和類型標識符文檔中描述的可用單位要相同。例如:HKQuantityTypeIdentifierHeight 文檔中說明它使用長度單位,因此,你的數量必須使用厘米、米、英尺、英寸或者其他長度單位。

 

這裡寫圖片描述

對應類別樣本,需要創建HKCategorySample的實例。它的值必須和類型標識符文檔中描述的枚舉值相關。例如, HKCategoryTypeIdentifierSleepAnalysis 文檔中說明它使用的枚舉值。因此你在創建樣本時必須從這個枚舉中傳遞一個值。
這裡寫圖片描述

同樣,你必須先創建correlation包含的所有樣本。correlation的類型標識符描述了它可以包含的類型和對象的數量。不要把被包含的對象存進HealthKit。它們是以correlation的一部分存儲的。

 

每日更新關注:http://weibo.com/hanjunqiang 新浪微博!iOS開發者交流QQ群: 446310206

這裡寫圖片描述

 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrbU09rRtcG3u+62r9H5sb6jrMrXz8ijrLS0vaggSEtXb3Jrb3V0VHlwZSDKtcD9sqKyu9Do0qrWuLaowODQzbHqyra3+6Gjy/nT0LXEd29ya291dLa8ysfTw82s0fm1xMDg0M2x6sq2t/uho7Xatv6jrLbU09rDv7j2d29ya291dMTjtrzQ6NKqzOG5qdK7uPYgSEtXb3Jrb3V0QWN0aXZpdHlUeXBlINa1oaPV4rj21rW2qNLlwct3b3Jrb3V01tDWtNDQtcS77ravtcTA4NDNoaPX7rrzo6y1sXdvcmtvdXSxo7Tmtb1IZWFsdGhLaXS686OsxOO/ydLUuPh3b3Jrb3V0udjBqrbuzeK1xNH5sb6ho9Xi0KnR+bG+zOG5qcHLd29ya291dLXEz+rPuNDFz6KhozwvcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160921/20160921092807350.png" title="\" />

HKQuery

HeathKit提供了許多查詢讀取數據的方法:

直接方法查詢。對於特征樣本,可以直接查詢獲取到,這些方法只能查詢特征樣本。更多信息: HKHealthStore Class Reference

樣本查詢。這是使用最多的查詢。使用樣本查詢可以查詢在HeathKit中任意的數據。而且可以對結果進行排序等。更多信息:HKSampleQuery Class Reference

觀察者查詢。這是一個長時間運行的查詢,它會檢測HealthKit存儲,並在匹配到的樣本發生變化時通知你。如果當存儲發生變化時你想得到通知,就使用觀察者查詢。更多信息:HKObserverQuery Class Reference

錨定對象查詢。用這種查詢來搜索添加進存儲的項。當錨定查詢第一次執行時,會返回存儲中所有匹配的樣本。在接下來的執行中,只會返回上一次執行之後添加的項目。通常,錨定對象查詢會和觀察者查詢一起使用。觀察者查詢告訴你某些項目發生了變化,而錨定對象查詢來決定有哪些(如果有的話)項目被添加進了存儲。更多信息:HKAnchoredObjectQuery Class Reference

統計查詢。使用這種查詢來在一系列匹配的樣本中執行統計運算。你可以使用統計查詢來計算樣本的總和、最小值、最大值或平均值。更多信息: HKStatisticsQuery Class Reference

統計集合查詢。使用這種查詢來在一系列長度固定的時間間隔中執行多次統計查詢。通常使用這種查詢來生成圖表。查詢提供了一些簡單的方法來計算某些值,例如,每天消耗的總熱量或者每5分鐘行走的步數。統計集合查詢是長時間運行的。查詢可以返回當前的統計集合,也可以監測HealthKit存儲,並對更新做出響應。更多信息,參見 HKStatisticsCollectionQuery Class Reference。

Correlation查詢。使用這種查詢來在correlation查找數據。這種查詢可以為correlation中每個樣本類型包含獨立的謂詞。如果你只是想匹配correlation類型,那麼請使用樣本查詢。更多信息,參見 HKCorrelation Class Reference。

來源查詢。使用這種查詢來查找HealthKit存儲中的匹配數據的來源(應用和設備)。來源查詢會列出儲存的特定樣本類型的所有來源。更多信息,參見HKSourceQuery Class Reference。

每日更新關注:http://weibo.com/hanjunqiang 新浪微博!iOS開發者交流QQ群: 446310206


HKUnit

這個類代表要查詢的數據的單位的類,比如體重的單位,可以為kg、lbs等。這個類為不同的數據類型提供了不同的單位方法。一般在創建前面介紹的樣本類型的時候,都需要這個類為樣本添加對應的單位。而且提供了一些數學運算,比如千米、米、厘米等之間的轉換。

在某些場合,你可以使用格式化器來本地化數量。iOS8提供了提供了新的格式化器來處理長度(NSLengthFormatter)、質量(NSMassFormatter)和能量(NSEnergyFormatter)。對於其他的數量,你需要自己來換算單位和本地化數據。

HKHeathStore

HeathKit的核心就是它,它代表HeathKit的數據庫,使用它就可以從數據庫中讀取數據。比較重要的方法:

isHealthDataAvailable:判斷當前設置是否支持HeathKitrequestAuthorizationToShareTypes(typesToShare: Set?, readTypes typesToRead: Set?, completion: (Bool, NSError?) -> Void): 向用戶請求同意讀寫某些數據saveObject(object: HKObject, withCompletion completion: (Bool, NSError?) -> Void) :向數據庫中添加數據executeQuery(query: HKQuery) :執行查詢,即上面介紹的幾種查詢方法。

HeathKit使用

在使用HealthKit之前,必須要執行下列步驟:

打開HeathKit,在Target欄中,打開Capabilities菜單,將HealthKit這一項的開關設為ON的狀態。

創建HeathManager.Swift 文件,並導入

`import HeathKit`

HeathKit的核心是HeathStore,創建

func authorizeHealthKit(completion:((success:Bool,error:NSError!)->Void)!){}

然後調用在這個方法中調用isHealthDataAvailable判斷當前設備是否支持HeathKit

//判斷當前設備是否支持
if !HKHealthStore.isHealthDataAvailable(){
    let error = NSError(domain: "", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"])
    if completion != nil {
        completion(success: false, error: error)
    }
}


,最後在上面的方法中,設置要讀寫的數據類型。

為你的應用實例化一個 HKHealthStore 對象。每個應用只需要一個HealthKit存儲實例。這個存儲實例就是你和HealthKit數據庫交互的主要接口。

let hkHealthStore = HKHealthStore()

使用 requestAuthorizationToShareTypes:readTypes:completion:來認證請求從HeathKit獲取數據的權限。


//請求連接 hkHealthStore.requestAuthorizationToShareTypes(healthKitTypesToWrite as? Set, readTypes: healthKitTypesToRead as? Set) { (success, error) -> Void in if completion != nil{ completion(success:success,error:error) } return }

如果當前設備支持HeathKit的時候,這樣就會彈出一個請求界面,讓用戶選擇是否同意你能夠獲取到你要請求的數據。

獲取特征信息

我們首先創建了ProfileViewController.swift,並用IB創建一個請求個人信息的界面
這裡寫圖片描述

然後在HeathManager.Swift 文件中添加請求個人信息的方法。
對於請求特征信息,前提上用戶通過健康添加了出生日期、性別、血型等特征信息

 

func readProfile()->(age:Int?,biologicalsex:HKBiologicalSexObject?,bloodType:HKBloodTypeObject?){

    //請求年齡
    var age:Int?
    let birthDay:NSDate;
    do {
        birthDay = try hkHealthStore.dateOfBirth()
        let today = NSDate()
        let diff = NSCalendar.currentCalendar().components(.Year, fromDate: birthDay, toDate: today, options: NSCalendarOptions(rawValue: 0))
        age = diff.year
    }catch {

    }
    //請求性別
    var biologicalSex
    :HKBiologicalSexObject?
    do {
        biologicalSex  = try hkHealthStore.biologicalSex()

    }catch {

    }    
    //請求血型
    var hkbloodType:HKBloodTypeObject?

    do {
        hkbloodType = try hkHealthStore.bloodType()
    }catch{

    }

    return (age,biologicalSex,hkbloodType)
}


 

請求體重、身高、BMI的時候,創建另外的方法。

 

func fetchMostRecentSample(sample:HKSampleType,competion:((HKSample!,NSError!)->Void)!){
    //1.創建謂詞
    let past = NSDate.distantPast()
    let now = NSDate()
    let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate: now, options: .None)

    //2.創建返回結果排序的描述,是降序還是升序的,因為只需要一個結果,就設定限制為1個
    let sortDescrptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate , ascending: false)
    let limit = 1

    //3.創建HKSampleQuery對象,
    let sampleQuery = HKSampleQuery(sampleType: sample, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescrptor]) { (sampleQuery, results, error) -> Void in

        if let queryError = error {
            competion(nil,queryError)
            return
        }

        let mostRecentSample = results?.first

        if competion != nil{
            competion(mostRecentSample,nil)
        }

    }

    //4.執行查詢
    self.hkHealthStore.executeQuery(sampleQuery)
}


 

獲取之後在之前創建的ProfileViewController.swift文件中獲取這些信息,並更新UI。

對應特征信息,可以直接調用查詢方法,並更新

 

    let  profile = healthManager?.readProfile()
    self.healthStore = HKHealthStore()

    ageLabel.text = profile?.age == nil ? kUnKnowString:String(profile!.age!)
    sexLabel.text = biologicSexLiteral(profile?.biologicalsex?.biologicalSex)
    bloodTypeLabel.text = bloodTypeLiteral(profile?.bloodType?.bloodType)


 

這裡面創建了兩個工具方法biologicSexLiteral和bloodTypeLiteral來修改查詢的結果為我們想要的樣子並顯示在界面上。

對於體重和身高,需要創建樣本查詢

 

/**
 獲取並更新體重
 */
func updateWeight(){ 
    let weightSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
    self.healthManager?.fetchMostRecentSample(weightSampleType!, competion: { (mostRecentSample, error) -> Void in

        if error != nil {
            return
        }

        var weightString = self.kUnKnowString
        self.weight = mostRecentSample as? HKQuantitySample

        //根據我們想要的數據類型單位獲取對應的結果
        if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(HKMetricPrefix.Kilo)){
            //體重格式化
            let weightFommater = NSMassFormatter()
            weightFommater.forPersonMassUse = true
            weightString = weightFommater.stringFromKilograms(kilograms)
        }

        //因為這個查詢默認是異步查詢的,所以需要在主線程更新UI
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.weightLabel.text = weightString
            self.updateBMILabel()
        }

    })
}

/**
 獲取並更新身高
 */
func updateHeight(){
    //設置要查找的類型,根據標識符
    let heightSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

    //獲取身高樣本
    self.healthManager?.fetchMostRecentSample(heightSampleType!, competion: { (heightSample, error) -> Void in

        if error != nil {
            return
        }

        var heightStr = self.kUnKnowString
        self.height = heightSample as? HKQuantitySample

        //根據我們想要的數據類型單位獲取對應的結果
        if let kilograms =  self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit()){

            heightStr = String(format: "%.2f", kilograms) + "m"
        }

        //因為這個查詢默認是異步查詢的,所以需要在主線程更新UI
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.heightLabel.text = heightStr
            self.updateBMILabel()
        }

    })
}


 

對應BMI,它代表人的身體質量指數,它的計算方式是:體重/(身高*身高)。因此它可以這樣獲得

 

/**
 獲取並設置BMI:
 */
func updateBMILabel(){

    //根據我們想要的數據類型單位獲取對應的結果
    let weight = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(HKMetricPrefix.Kilo))
    let height = self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit())
    var bmiValue = 0.0
    if height == 0{

        return
    }

    dispatch_async(dispatch_get_main_queue()) { () -> Void in
        bmiValue = (weight!)/(height! * height!)
        self.BMILabel.text = String(format: "%.02f", bmiValue)
    } 
}


 

添加BMI到HeathStore

在下面的方法中添加一個alertView讓用戶輸入BMI值,然後點擊確認按鈕之後添加到HeathStore中

 

@IBAction func addBMIData2HealthStore(sender: AnyObject) {        
    let alertView = UIAlertController(title: "輸入BMI值", message: nil, preferredStyle: .Alert)

    alertView.addTextFieldWithConfigurationHandler { (textField) -> Void in
        textField.keyboardType = .NumberPad
    }

    let action =  UIAlertAction(title: "添加", style: .Default) { (action) -> Void in

        var value:Double?

        if let text = alertView.textFields?.first?.text {
            if text.characters.count > 0 {
                value = Double(text)
                self.saveBMI2HealthStore(value!)
            }
        }
    }
    alertView.addAction(action)

    self .presentViewController(alertView, animated: true, completion: nil)
}


//保存BMI到heathKitStore中
func saveBMI2HealthStore(height:Double){

    //BMI的類型
    let BMIType =  HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)

    //根據標識符對應的單位創建BMI的數量對象
    let BMIQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: height)

    let now = NSDate()

    //根據起止時間以及上面創建的創建HKQuantity對象創建數量樣本
    let BMISample =  HKQuantitySample(type: BMIType!, quantity: BMIQuantity, startDate: now, endDate: now)

    //保存數量樣本到healthStore中
    self.healthStore?.saveObject(BMISample, withCompletion: { (success, error) -> Void in

        if success {
            print("添加成功")
            self.updateWeight()
        }

        if (error != nil) {
            print("添加失敗")
        }
    })
}


 

如果添加成功,你就可以去手機上的健康查找BMI,就可以看到我們剛才添加的BMI值,而且它的來源是我們創建的app。

獲取HKWorkout

創建一個WorkOutsViewController.swift文件,並在SB中拖對應的IB文件,界面如下
這裡寫圖片描述

然後在在HeathManager.Swift 文件中添加請求workout的方法

/**
 獲取workoutData
 */
func fetchWorkOutsData(completion:([AnyObject]!,NSError!)->Void){

    let workOutsSampleType = HKSampleType.workoutType()

    let workOutsPredicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(.Running)

    let sortDescrptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate , ascending: false)

    let workOutsQuery = HKSampleQuery(sampleType: workOutsSampleType, predicate: workOutsPredicate, limit: 0, sortDescriptors: [sortDescrptor]) { (workoutsQuery, results, error) -> Void in

        if (error != nil){
            print("獲取失敗")
            return
        }

        if results != nil{
            completion(results!,nil)
        }

    }

    self.hkHealthStore.executeQuery(workOutsQuery)

}


 

然後在WorkOutsViewController.swift文件的viewWillAppear()方法中請求workout

 

self.healthManager?.fetchWorkOutsData({ (results, error) -> Void in

        if error != nil{
            print("獲取失敗")
        }else{
            self.workOuts = results as! [HKWorkout]
        }

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.tableView.reloadData()
        });
    })


 

最後在tableView顯示如下
這裡寫圖片描述

保存HKWorkout

在上面的界面的NavgationBar的rightBarItem向下再拖一個控制器,並添加對應的文件AddWorkoutsViewController.swift,並在IB中設置界面信息如下
這裡寫圖片描述

然後在 AddWorkoutsViewController.swift 復寫 tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath),針對點擊不同的cell,執行不同的方法。即讓用戶輸入點擊的cell對應的輸入方式,比如時間就是時間選擇器。距離就是一個警示框加一個文本框等。

 

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    tableView.deselectRowAtIndexPath(indexPath, animated: true)

    self.tableView.tableFooterView = UIView()

    switch indexPath.row{

    case 0:
        self.setupPickerView()

    case 1,2:
        self.setupDatePickerView(indexPath.row)

    case 3,4:
        self.setupAlertView(indexPath.row)
    default:
        break
    }
}


 

這裡面對應的選擇的方法就不一一介紹了,就是幾個普通的view的添加。添加完所有的信息之後就可以點擊done 保存信息,方法如下:

 

 @IBAction func addWorkOut(sender: AnyObject) {

    self.heathStore = HKHealthStore()

    //獲取距離和能量的數值
    let distanceValue = Double(self.distanceLabel.text!)
    let energyValue = Double(self.energyLabel.text!)

    //根據上面的數值創建對應的HKQuantity對象
    let distance = HKQuantity(unit: HKUnit.mileUnit(), doubleValue: distanceValue!)
    let energy = HKQuantity(unit: HKUnit.calorieUnit(), doubleValue: energyValue!)

    let endDate = self.dateFommater?.dateFromString(self.endDateLabel.text!)
    let startDate = self.dateFommater?.dateFromString(self.startDateLabel.text!)

    //這裡我默認設置成running了。可以根據具體的類型再進行設置。
    //創建HKWorkout對象。
    let workout = HKWorkout(activityType: .Running, startDate: startDate!, endDate: endDate!, workoutEvents: nil, totalEnergyBurned: energy, totalDistance: distance, metadata: nil)

    //保存上面創建的HKWorkout對象
    self.heathStore?.saveObject(workout, withCompletion: { (success, error) -> Void in
        if error != nil{
            print("添加錯誤")
            return
        }

        if success{
            print("添加成功")
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.dismissViewControllerAnimated(true, completion: nil)
            })
        }

    })
}


 

如果上面都執行成功,AddWorkoutsViewController.swift就會模態消失,然後在上面的一個頁面`WorkOutsViewController.swift就會在tableView的最上層顯示出我們剛才添加成功的HKWorkout

總結

在本人過完春節回到公司上班之後經理問我健康app裡面的信息能不能獲取到。之前只是簡單了解了這個框架,但是裡面的具體結構體系並不了解。就趁著項目不忙,抽空把HeathKit學習了解了一下。本文的demo也采用了之前自學的Swift簡單的實現了一下(屬於Switer新手)。可能會有錯誤或不准確的地方,如果你看到了,可以給我聯系[email protected],我會及時更改的。寫這篇文章一是對HeathKit的學習的一個練習,在這也是給以後會用到的童鞋一個可以參考的東西。

HeathKit不只是上面的這些內容,但是能把上面的這些問題搞定,我覺得針對HeathKit的體系會有一個清楚的認識,學習HeathKit更深層次的內容會有很大的幫助。

本文的demo已經放到github上面,需要的同學可以下載看看。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved