最近開始跑步了,天天看到「健身紀錄」的圓圈,挺有感觸的。html
天天的「圈」裏能看到當天的活動量、鍛鍊時長、站立時間。ios
打開詳情後,還能看到跑步步數、跑步距離、以及從第三方同步過來的數據等。git
今天咱們的目標是拿到第一個量化數據:「健身記錄」的圓圈數據,即 iPhone 提供的 HealthKit data 數據導出,放入第三方數據庫中,以供後續統計和分析。github
簡單看看個人「健康」APP 的數據:
數據庫
有了健康 app,你能夠將各類健康和健身信息都保存在一個地方,讓它們盡在你的掌控。保存哪些信息,以及哪些 app 能夠經過健康 app 訪問你的數據都由你決定。當你使用密碼、觸控 ID 或面容 ID 鎖定手機時,健康 app 中全部的健康和運動數據都將被加密,只有醫療急救卡中的信息除外。你能夠經過 iCloud,讓健康數據自動在你的各類設備上保持更新,包括傳輸和存儲過程都會被加密保護。此外,訪問 HealthKit 的 app 都必須備有隱私政策,所以在受權這些 app 訪問你的健康和健身數據以前,請務必仔細查看這些政策。swift
咱們再來看看平時都有哪些第三方 app 受權訪問咱們的健康數據:api
數據來源有了,接下來就是考慮如何將健康數據導出。經過對 HealthKit 的瞭解,咱們須要本身動手開發一個 iOS app,獲取健康數據,並上傳到咱們的服務器上,達到數據導出的目標。數組
這張「圈」圖是我 11 月 9 日的數據,今天的目標就是經過 HealthKit 獲取這個圈的數據,主要包括:服務器
- 活動數據:1048 大卡
- 鍛鍊時間:92 分鐘
- 站立時間:11 小時
在建立 iOS 應用 id 時,須要擁有「HealthKit」能力:app
在 Info.plist
配置中增長如下兩項內容:
參考 HealthKit 開發文檔,要獲取「圈」運動統計數據,須要利用 Activity summary query
即,HKActivitySummaryQuery
查詢。
結果字段類型爲:HKActivitySummary
。
HKActivitySummaryQueryA query for read activity summary objects from the HealthKit store.
參考:https://developer.apple.com/documentation/healthkit/hkactivitysummaryquery
HKActivitySummary
An object that contains the move, exercise, and stand data for a given day.
參考:https://developer.apple.com/documentation/healthkit/hkactivitysummary
瞭解了 HKActivitySummaryQuery
和 HKActivitySummary
,咱們就能夠進入開發了。
這裏我主要藉助「MBHealthTracker」,MBHealthTracker 封裝好了和 HealthKit 交互的受權,獲取數據等,只要直接調用便可。
MBHealthTracker Github: https://github.com/matybrennan/MBHealthTracker
當我發現 MBHealthTracker 沒有對應的功能獲取 HKActivitySummary
功能,因此須要咱們在 MBHealthTracker 基礎上增長杜對應的獲取方法。
先在 Presentation
下增長 ActivitySummary
模型,主要是建立數組。
import Foundation import HealthKit public struct ActivitySummary { public let items: [HKActivitySummary] }
接着在 Business Logic
邏輯層,建立協議和實現方法:
// ActivitySummaryServiceProtocol.swift import Foundation import HealthKit public protocol ActivitySummaryServiceProtocol { // 根據起止時間獲取 ActivitySummary func getActivitySummary(startDate: Date, endDate: Date, completionHandler: @escaping (MBAsyncCallResult<ActivitySummary>) -> Void) throws } // ActivitySummaryService.swift import Foundation import HealthKit class ActivitySummaryService { public init() { } } extension ActivitySummaryService: ActivitySummaryServiceProtocol { func getActivitySummary(startDate: Date, endDate: Date, completionHandler: @escaping (MBAsyncCallResult<ActivitySummary>) -> Void) throws { try isDataStoreAvailable() // Create the date components for the predicate guard let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian) else { fatalError("*** This should never fail. ***") } let units: NSCalendar.Unit = [.day, .month, .year, .era] var startDateComponents = calendar.components(units, from: startDate) startDateComponents.calendar = calendar as Calendar var endDateComponents = calendar.components(units, from: endDate) endDateComponents.calendar = calendar as Calendar // Create the predicate for the query let summariesWithinRange = HKQuery.predicate(forActivitySummariesBetweenStart: startDateComponents, end: endDateComponents) // Build the query let query = HKActivitySummaryQuery(predicate: summariesWithinRange) { (query, summaries, error) -> Void in self.configure(query: query, summaries: summaries, error: error, completionHandler: completionHandler) } healthStore.execute(query) } } private extension ActivitySummaryService { func configure(query: HKActivitySummaryQuery, summaries: [HKActivitySummary]?, error: Error?, completionHandler: @escaping (MBAsyncCallResult<ActivitySummary>) -> Void) { guard error == nil else { completionHandler(.failed(error!)) return } let activitySummary = ActivitySummary(items: summaries!) completionHandler(.success(activitySummary)) } }
參考官網的 demo 和 MBHealthTracker 的方法,依葫蘆畫瓢寫好實現類,代碼簡單,就不詳細說明。
剩下的就是在 MBHealthTrackerProtocol
和 MBHealthTracker
載入方法便可。
// MBHealthTrackerProtocol.swift var activitySummary: ActivitySummaryServiceProtocol { get } // MBHealthTracker.swift private lazy var privateActivitySummaryService: ActivitySummaryServiceProtocol = { return ActivitySummaryService() }() ... public var activitySummary: ActivitySummaryServiceProtocol { return privateActivitySummaryService }
獲取數據的部分暫時告一段落,下一步就是要經過配置,拿到受權能夠讀取 ActivitySummary 數據。
/// Just has read capabilities public enum MBReadType: ReadableType { // Characteristics case dob case gender case activitySummary public var readable: HKObjectType { switch self { case .dob: return HKCharacteristicType.characteristicType(forIdentifier: .dateOfBirth)! case .gender: return HKCharacteristicType.characteristicType(forIdentifier: .biologicalSex)! case .activitySummary: return HKActivitySummaryType.activitySummaryType() } } }
萬事俱備,咱們測試下看看運行結果。
import Foundation import HealthKit protocol ViewInteractorProtocol { func configurePermissions() func runTest() } class ViewInteractor { private let healthTracker: MBHealthTrackerProtocol init(healthTracker: MBHealthTrackerProtocol) { self.healthTracker = healthTracker } } // MARK: - ViewInteractorProtocol extension ViewInteractor: ViewInteractorProtocol { // 配置寫入和讀取受權的類目 func configurePermissions() { healthTracker.configuration.requestAuthorization(toShare: [] ,toRead: [MBReadType.activitySummary]) { _ in } } // 測試,看看11-9號到11-3號得數據 func runTest() { do { print("-----------------get summary begin----------------") let date = Date() let start = date.parse("2019-11-09") let end = date.parse("2019-11-13") try healthTracker.activitySummary.getActivitySummary(startDate: start, endDate: end, completionHandler: { (result) in print(result) }) print("-----------------get summary end----------------") } catch { print("Unable to get: \(error.localizedDescription)") } } }
運行打印出來的結果:
success(Lianghua.ActivitySummary(items: [<<HKActivitySummary: 0x2832480c0>: Date=(Year: 2019, Month: 11, Day: 9) Active Energy Burned=(1048.476259360438/310) Apple Exercise Minutes=(92/30) Apple Stand Hours=(11/12)>, <<HKActivitySummary: 0x283248180>: Date=(Year: 2019, Month: 11, Day: 10) Active Energy Burned=(474.8101270220084/310) Apple Exercise Minutes=(33/30) Apple Stand Hours=(6/12)>, <<HKActivitySummary: 0x283240f00>: Date=(Year: 2019, Month: 11, Day: 11) Active Energy Burned=(357.55/310) Apple Exercise Minutes=(22/30) Apple Stand Hours=(16/12)>, <<HKActivitySummary: 0x283241200>: Date=(Year: 2019, Month: 11, Day: 12) Active Energy Burned=(344.8089999999997/310) Apple Exercise Minutes=(17/30) Apple Stand Hours=(16/12)>, <<HKActivitySummary: 0x2832412c0>: Date=(Year: 2019, Month: 11, Day: 13) Active Energy Burned=(181.595/310) Apple Exercise Minutes=(21/30) Apple Stand Hours=(4/12)>]))
這裏咱們對照開篇的「圈」圖,和這裏的數據徹底一致。
咱們導出健康數據後,就要考慮統一存放到雲平臺或者第三方存儲平臺上,以供後續統計分析。具體選擇什麼平臺來存儲數據呢,咱們下期再聊!
未完待續
參考: