【編者按】本文作者為 Matthew Maher,文章手把手地介紹了如何借助 HealthKit 建立簡單的健身應用,包含諸多代碼實例。本文系國內 ITOM 管理平臺 OneAPM 編譯呈現。
根據新聞報導,健康與健美在今時今日的重要程度比已往任何時候都高。說起來有點可笑,似乎就在幾天之前,筆者就見到過類似的新聞。或許,這是當人逐漸變老之后揮之不去的感覺吧——渴望保持健康以及健美的感覺。不管怎么說,健康與健美是一個重要話題。技術的進步,尤其是移動應用與硬件世界的不斷提高,正為這個似乎日益成長的話題帶來全新的契機。
HealthKit 是蘋果公司推出的一款移動應用平臺,旨在為重要、可追蹤的健康數據與注重健康、熱衷鍛煉的科技消費者搭起橋梁。這很酷。用戶可以輕松地追蹤一段時間內可測量的健身與健康數據。除了了解自身的健康數據,看到圖表中喜人的增長曲線也的確鼓舞人心。
正如人們想象的那樣,在管理健康信息時安全是非常重要的考慮因素。HealthKit 直截了當地將所有 HealthKit 信息的絕對控制權置于用戶的手中。用戶可以授權或拒絕任何應用對其健康數據發(fā)出的讀取請求。
作為開發(fā)者,我們需要征求許可才能從/向 HealthKit 讀取/寫入數據。實際上,我們需要明確地聲明打算讀取或改變的數據。此外,任何使用 HealthKit 的應用都必須包含隱私政策,這樣一來,用戶才能對其信息的處理感到更加放心。
關于 OneHourWalker
在本文中,我們將打造一個有趣的小應用,它會從 HealthKit 讀取數據,也會向其寫入新數據。來見一見 OneHourWalker 吧。
OneHourWalker 是一款追蹤使用者在一個小時內行走或跑步之距離的健身應用。用戶可以將距離與 HealthKit 分享,之后就能在健康應用中讀取之。我知道,一個小時聽起來有點過于樂觀了(至少筆者本人可能無法堅持下去)。因此,用戶也可以提早中止計數,并分享距離。
額,到目前為止,似乎 OneHourWalker 只會向 HealthKit 寫入數據。我們需要讀取什么數據呢?
好問題!在步行鍛煉時,我喜歡選擇鄉(xiāng)間或林間小路。常常,我會遇到樹枝低垂的區(qū)域。而我是一條身高 193cm 的漢子,這真的讓我很苦惱。解決辦法是:從 HealthKit 讀取用戶的身高數據,將之打印為應用的一個標簽。這個標簽可以作為對用戶的善意提醒,這樣,他們就能避免在步行時被樹枝打到。
首先,點此下載 OneHourWalker 的初始項目。先試著跑起來,找找應用運行的感覺。計數器與地點追蹤功能已經在運行了,所以我們只需專注于 HealthKit 實現。注意,當到達 60 分鐘時間點時,計算器與追蹤都會停止。
啟用 HealthKit
首先,在我們的應用中啟用 HealthKit。在項目導航中,點擊 OneHourWalker,之后點擊 Targets 下面的 OneHourWalker,之后選擇屏幕頂部的 Capabilities 選項。
查看 Capabilities 列表的底部,啟用 HealthKit
。這一簡單的操作會將 HealthKit 權限添加到 App ID,將 HealthKit 鍵添加到 info plist 文件,將 HealthKit 權限添加到授權文件,并且與 HealthKit.framework
相連接。就是這么簡單。
開始編程
接下來,跳轉到 TimerViewController.swift
,開始將 HealthKit 引入 OneHourWalker。首先,創(chuàng)建一個 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 中,我們會創(chuàng)建 authorizeHealthKit() 方法。然而,除此之外,我們需要創(chuàng)建 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
中,創(chuàng)建從 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)
}
查詢身高數據的第一步是創(chuàng)建一個斷言以定義時間參數。我們是在請求一段時間內的所有身高數據——與當前日期相距甚遠的一個過去的日期。顯然,這會返回一個數組。然而,我們只想要最近期的身高,因此,我們請求數據時可以讓最新的數據排在數組的最前頭。
在構建這一查詢時,我們會把數組的長度限制為1。在考慮好出現錯誤的可能性后,我們會將結果中的首個也即唯一一個數組項目分配給 lastHeight。接下來,完善 getHeight() 方法。最后,針對用戶的健康數據執(zhí)行查詢。
回到 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()
方法之上,我們會創(chuàng)建 setHeight()
方法。我們請求的身高數據樣本以 HKQuantity
返回,標識符 HKQuantityTypeIdentifierHeight
知道這一對象。
接下來,調用在 manager 中創(chuàng)建的 getHeight()
方法。有了身高樣本,我們還需要將之翻譯為恰當的字符串以展示在標簽中。與往常一樣,考慮所有可能的錯誤情況是很重要的。
到此,用戶就可以打開 app,查看他們的身高(如果他的健康應用中記錄著身高數據),開啟計時器,追蹤他跑步或行走的距離了。接下來,我們要處理將距離數據寫入健康應用的過程,這樣,用戶才能在同一個應用中保存其所有的健身數據。
在用戶結束外出鍛煉之后,不管有沒有到60分鐘,他可能會使用 Share(分享)按鈕將其辛苦賺得的運動距離發(fā)送到健康應用。所以,在 share() 方法中,我們需要調用 HealthKitManager.swift
的 saveDistance()
方法來實現這一過程。在這個方法中,我們會發(fā)送運動距離以及取得該距離的日期。這樣,用戶便能在第二天爭取更好的成績。
@IBAction func share(sender: AnyObject) {
healthManager.saveDistance(distanceTraveled, date: NSDate())
}
接下來,回到 manager,我們要在此處創(chuàng)建 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(顯示所有數據)。我們的數據就在此列表中。輕擊一個單元,我們的圖標(目前還未設置)就會與距離一同出現。再次點擊此單元,就能看到完整的細節(jié)數據。
借助 OneHourWalker,我們便能為全世界 iOS 用戶的身體健康貢獻一份力量。然而,這只是一個開始。在使用 HealthKit 讀取并修改健康數據的道路上,還有非常多的可能性。
當然,對用戶而言,擁有這些可追蹤數據的好處很多。人們可以輕松地按照日期、星期進行比較,從而激勵自己朝著目標努力。不過,真正的偉大之處在于,開發(fā)者可以提供全新的,富有創(chuàng)造力的有趣方法來獲取數據。
歡迎大家對 HealthKit 應用進行測試。點擊此處查看 OneHourWalker 的最終版本。
本文系 OneAPM 工程師編譯整理。OneAPM Mobile Insight 以真實用戶體驗為度量標準進行 Crash 分析,監(jiān)控網絡請求及網絡錯誤,提升用戶留存。訪問 OneAPM 官方網站感受更多應用性能優(yōu)化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。
本文轉自 OneAPM 官方博客