如何藉助 HealthKit 打造一款健身應用?

【編者按】本文做者爲 Matthew Maher,文章手把手地介紹瞭如何藉助 HealthKit 創建簡單的健身應用,包含諸多代碼實例。本文系國內 ITOM 管理平臺 OneAPM 編譯呈現。html

根據新聞報導,健康與健美在今時今日的重要程度比已往任什麼時候候都高。提及來有點好笑,彷佛就在幾天以前,筆者就見到過相似的新聞。或許,這是當人逐漸變老以後揮之不去的感受吧——渴望保持健康以及健美的感受。無論怎麼說,健康與健美是一個重要話題。技術的進步,尤爲是移動應用與硬件世界的不斷提升,正爲這個彷佛日益成長的話題帶來全新的契機。ios

HealthKit 是蘋果公司推出的一款移動應用平臺,旨在爲重要、可追蹤的健康數據與注重健康、熱衷鍛鍊的科技消費者搭起橋樑。這很酷。用戶能夠輕鬆地追蹤一段時間內可測量的健身與健康數據。除了瞭解自身的健康數據,看到圖表中喜人的增加曲線也的確鼓舞人心。git

正如人們想象的那樣,在管理健康信息時安全是很是重要的考慮因素。HealthKit 直截了當地將全部 HealthKit 信息的絕對控制權置於用戶的手中。用戶能夠受權或拒絕任何應用對其健康數據發出的讀取請求。github

做爲開發者,咱們須要徵求許可才能從/向 HealthKit 讀取/寫入數據。實際上,咱們須要明確地聲明打算讀取或改變的數據。此外,任何使用 HealthKit 的應用都必須包含隱私政策,這樣一來,用戶才能對其信息的處理感到更加放心。編程

##關於 OneHourWalker 在本文中,咱們將打造一個有趣的小應用,它會從 HealthKit 讀取數據,也會向其寫入新數據。來見一見 OneHourWalker 吧。swift

如何藉助 HealthKit 打造一款健身應用?

OneHourWalker 是一款追蹤使用者在一個小時內行走或跑步之距離的健身應用。用戶能夠將距離與 HealthKit 分享,以後就能在健康應用中讀取之。我知道,一個小時聽起來有點過於樂觀了(至少筆者本人可能沒法堅持下去)。所以,用戶也能夠提前停止計數,並分享距離。數組

額,到目前爲止,彷佛 OneHourWalker 只會向 HealthKit 寫入數據。咱們須要讀取什麼數據呢?安全

好問題!在步行鍛鍊時,我喜歡選擇鄉間或林間小路。經常,我會遇到樹枝低垂的區域。而我是一條身高 193cm 的漢子,這真的讓我很苦惱。解決辦法是:從 HealthKit 讀取用戶的身高數據,將之打印爲應用的一個標籤。這個標籤能夠做爲對用戶的善意提醒,這樣,他們就能避免在步行時被樹枝打到。性能優化

首先,點此下載 OneHourWalker 的初始項目。先試着跑起來,找找應用運行的感受。計數器與地點追蹤功能已經在運行了,因此咱們只需專一於 HealthKit 實現。注意,當到達 60 分鐘時間點時,計算器與追蹤都會中止。網絡

##啓用 HealthKit 首先,在咱們的應用中啓用 HealthKit。在項目導航中,點擊 OneHourWalker,以後點擊 Targets 下面的 OneHourWalker,以後選擇屏幕頂部的 Capabilities 選項。

如何藉助 HealthKit 打造一款健身應用?

查看 Capabilities 列表的底部,啓用 HealthKit。這一簡單的操做會將 HealthKit 權限添加到 App ID,將 HealthKit 鍵添加到 info plist 文件,將 HealthKit 權限添加到受權文件,而且與 HealthKit.framework 相鏈接。就是這麼簡單。

##開始編程 接下來,跳轉到 TimerViewController.swift,開始將 HealthKit 引入 OneHourWalker。首先,建立一個 HealthKitManager 實例。

import UIKit
import CoreLocation
import HealthKit

class TimerViewController: UIViewController, CLLocationManagerDelegate {

    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var milesLabel: UILabel!
    @IBOutlet weak var heightLabel: UILabel!    

    var zeroTime = NSTimeInterval()    
    var timer : NSTimer = NSTimer()

    let locationManager = CLLocationManager()    
    var startLocation: CLLocation!    
    var lastLocation: CLLocation!    
    var distanceTraveled = 0.0

    let healthManager:HealthKitManager = HealthKitManager()

全部 HealthKit 工做都會在 HealthKitManager.swift 中進行。它會包含重要的方法,咱們很快就會談到。

正如在前文介紹部分所述,咱們須要取得用戶的許可,才能讀取並修改他們的健康數據。在 viewDidLoad()中,咱們就得這麼作。

override func viewDidLoad() {
       super.viewDidLoad()

   locationManager.requestWhenInUseAuthorization()

    if CLLocationManager.locationServicesEnabled(){
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
    } else {
        print("Need to Enable Location")
    }

    // We cannot access the user's HealthKit data without specific permission.
    getHealthKitPermission()
}

getHealthKitPermission() 方法會調用 manager 的 authorizeHealthKit()方法。若是一切順利,咱們便能調用setHeight()方法。不過,咱們很快會在後文中談到此方法。

func getHealthKitPermission() {        

    // Seek authorization in HealthKitManager.swift.
    healthManager.authorizeHealthKit { (authorized,  error) -> Void in                        if authorized {                

            // Get and set the user's height.
            self.setHeight()
        } else {                
                   if error != nil {                    
                        print(error)
            }                
            print("Permission denied.")
        }
    }
}

在 HealthKitManager.swift 中,咱們會建立 authorizeHealthKit() 方法。然而,除此以外,咱們須要建立 HealthKit 存儲,用於鏈接應用與 HealthKit 的數據。

let healthKitStore: HKHealthStore = HKHealthStore()

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

    // State the health data type(s) we want to read from HealthKit.        
    let healthDataToRead = Set(arrayLiteral: HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!)

    // State the health data type(s) we want to write from HealthKit.        
    let healthDataToWrite = Set(arrayLiteral: HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!)

    // Just in case OneHourWalker makes its way to an iPad...        
    if !HKHealthStore.isHealthDataAvailable() {            
        print("Can't access HealthKit.")
    }

    // Request authorization to read and/or write the specific data.        
    healthKitStore.requestAuthorizationToShareTypes(healthDataToWrite, readTypes: healthDataToRead) { (success, error) -> Void in            
    if( completion != nil ) {
            completion(success:success, error:error)
        }
    }
}

在請求獲取用戶健康數據的受權時,咱們須要明確指定打算讀取以及修改的信息。對本例而言,咱們須要讀取用戶的身高,從而幫助他們躲避有危險的低垂枝丫。咱們但願 HealthKit 能提供一個能夠轉化爲可理解的身高的 HKObject 量。此外,咱們還要得到修改 HKObject 量的許可,以記錄用戶的行走及跑步距離。

在處理好 OneHourWalker 與 iPad 通訊的可能性後,咱們作出官方請求。

HealthKitManager.swift 中,建立從 HealthKit 讀取用戶身高數據的 getHeight() 方法。

func getHeight(sampleType: HKSampleType , completion: ((HKSample!, NSError!) -> Void)!) {

    // Predicate for the height query        
    let distantPastHeight = NSDate.distantPast() as NSDate        
    let currentDate = NSDate()        
    let lastHeightPredicate = HKQuery.predicateForSamplesWithStartDate(distantPastHeight, endDate: currentDate, options: .None)

    // Get the single most recent height        
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)

    // Query HealthKit for the last Height entry.        
    let heightQuery = HKSampleQuery(sampleType: sampleType, predicate: lastHeightPredicate, limit: 1, sortDescriptors: [sortDescriptor]) { (sampleQuery, results, error ) -> Void in                

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

            // Set the first HKQuantitySample in results as the most recent height.                let lastHeight = results!.first                

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

    // Time to execute the query.
    self.healthKitStore.executeQuery(heightQuery)
}

查詢身高數據的第一步是建立一個斷言以定義時間參數。咱們是在請求一段時間內的全部身高數據——與當前日期相距甚遠的一個過去的日期。顯然,這會返回一個數組。然而,咱們只想要最近期的身高,所以,咱們請求數據時可讓最新的數據排在數組的最前頭。

在構建這一查詢時,咱們會把數組的長度限制爲1。在考慮好出現錯誤的可能性後,咱們會將結果中的首個也即惟一一個數組項目分配給 lastHeight。接下來,完善 getHeight() 方法。最後,針對用戶的健康數據執行查詢。

回到 TimerViewController.swift,在 app 真正投入使用以前,假設用戶受權了適當的許可,則 setHeight() 方法會被 getHealthKitPermission() 調用。

var height: HKQuantitySample?

首先,咱們須要爲 HKQuantitySample 實例聲明一個身高變量。

func setHeight() {        
     // Create the HKSample for Height.        
     let heightSample = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)        

     // Call HealthKitManager's getSample() method to get the user's height.              
     self.healthManager.getHeight(heightSample!, completion: { (userHeight, error) -> Void in            

     if( error != nil ) {
            print("Error: \(error.localizedDescription)")                
            return
        }            

        var heightString = ""            

        self.height = userHeight as? HKQuantitySample            

        // The height is formatted to the user's locale.            
        if let meters = self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit()) {                
            let formatHeight = NSLengthFormatter()
            formatHeight.forPersonHeightUse = true
            heightString = formatHeight.stringFromMeters(meters)
        }            

        // Set the label to reflect the user's height.
        dispatch_async(dispatch_get_main_queue(), { () -> Void in                                self.heightLabel.text = heightString
        })
    })

}

share() 方法之上,咱們會建立 setHeight() 方法。咱們請求的身高數據樣本以 HKQuantity 返回,標識符 HKQuantityTypeIdentifierHeight 知道這一對象。

接下來,調用在 manager 中建立的 getHeight() 方法。有了身高樣本,咱們還須要將之翻譯爲恰當的字符串以展現在標籤中。與往常同樣,考慮全部可能的錯誤狀況是很重要的。

到此,用戶就能夠打開 app,查看他們的身高(若是他的健康應用中記錄着身高數據),開啓計時器,追蹤他跑步或行走的距離了。接下來,咱們要處理將距離數據寫入健康應用的過程,這樣,用戶才能在同一個應用中保存其全部的健身數據。

在用戶結束外出鍛鍊以後,無論有沒有到60分鐘,他可能會使用 Share(分享)按鈕將其辛苦賺得的運動距離發送到健康應用。因此,在 share() 方法中,咱們須要調用 HealthKitManager.swiftsaveDistance() 方法來實現這一過程。在這個方法中,咱們會發送運動距離以及取得該距離的日期。這樣,用戶便能在次日爭取更好的成績。

@IBAction func share(sender: AnyObject) {    
      healthManager.saveDistance(distanceTraveled, date: NSDate())
}

接下來,回到 manager,咱們要在此處建立 saveDistance() 方法。首先,咱們要讓 HealthKit 知道咱們打算寫入一個表明步行及跑步距離的量。以後,將度量單位設置爲英里,並賦值官方的樣本量。HealthKit 的 saveObject() 方法會將此數據寫入用戶的健康數據。

func saveDistance(distanceRecorded: Double, date: NSDate ) {        

    // Set the quantity type to the running/walking distance.
    let distanceType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)        

    // Set the unit of measurement to miles.
    let distanceQuantity = HKQuantity(unit: HKUnit.mileUnit(), doubleValue: distanceRecorded)        

    // Set the official Quantity Sample.
    let distance = HKQuantitySample(type: distanceType!, quantity: distanceQuantity, startDate: date, endDate: date)        

    // Save the distance quantity sample to the HealthKit Store.
    healthKitStore.saveObject(distance, withCompletion: { (success, error) -> Void in            
    if( error != nil ) {                
          print(error)
        } else {                
          print("The distance has been recorded! Better go check!")
        }
    })
}

跳轉到健康應用,所記錄的數據會出如今 Walking + Running Distance(行走+跑步距離)一行(若是已經啓用)。此外,依照下面的路徑,咱們能夠看到詳細的樣本數據:Health Data tab(健康數據選項卡) > Fitness(健身) > Walking + Running Distance(行走+跑步距離) > Show All Data(顯示全部數據)。咱們的數據就在此列表中。輕擊一個單元,咱們的圖標(目前還未設置)就會與距離一同出現。再次點擊此單元,就能看到完整的細節數據。

如何藉助 HealthKit 打造一款健身應用?

藉助 OneHourWalker,咱們便能爲全世界 iOS 用戶的身體健康貢獻一份力量。然而,這只是一個開始。在使用 HealthKit 讀取並修改健康數據的道路上,還有很是多的可能性。

固然,對用戶而言,擁有這些可追蹤數據的好處不少。人們能夠輕鬆地按照日期、星期進行比較,從而激勵本身朝着目標努力。不過,真正的偉大之處在於,開發者能夠提供全新的,富有創造力的有趣方法來獲取數據。

歡迎你們對 HealthKit 應用進行測試。點擊此處查看 OneHourWalker 的最終版本。

本文系 OneAPM 工程師編譯整理。OneAPM Mobile Insight真實用戶體驗爲度量標準進行 Crash 分析,監控網絡請求及網絡錯誤,提高用戶留存。訪問 OneAPM 官方網站感覺更多應用性能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客

本文轉自 OneAPM 官方博客

原文地址:http://www.appcoda.com/healthkit-introduction/

相關文章
相關標籤/搜索