最近開始跑步了,天天看到「健身紀錄」的圓圈,挺有感觸的。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 獲取這個圈的數據,主要包括:bash
- 活動數據:1048 大卡
- 鍛鍊時間:92 分鐘
- 站立時間:11 小時
在建立 iOS 應用 id 時,須要擁有「HealthKit」能力:服務器
在 Info.plist
配置中增長如下兩項內容:
參考 HealthKit 開發文檔,要獲取「圈」運動統計數據,須要利用 Activity summary query
即,HKActivitySummaryQuery
查詢。
結果字段類型爲:HKActivitySummary
。
HKActivitySummaryQuery
A query for read activity summary objects from the HealthKit store.
參考:developer.apple.com/documentati…
HKActivitySummary
An object that contains the move, exercise, and stand data for a given day.
瞭解了 HKActivitySummaryQuery
和 HKActivitySummary
,咱們就能夠進入開發了。
這裏我主要藉助「MBHealthTracker」,MBHealthTracker 封裝好了和 HealthKit 交互的受權,獲取數據等,只要直接調用便可。
MBHealthTracker Github: github.com/matybrennan…
當我發現 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)>]))
複製代碼
這裏咱們對照開篇的「圈」圖,和這裏的數據徹底一致。
咱們導出健康數據後,就要考慮統一存放到雲平臺或者第三方存儲平臺上,以供後續統計分析。具體選擇什麼平臺來存儲數據呢,咱們下期再聊!
未完待續
參考: