首页 > 解决方案 > 从 Health App 实时获取新的心率数据?

问题描述

我正在尝试根据用户的实时心率数据构建 iPhone 应用程序。我能够从 Health App 获得最新的心率,但是一旦记录了新信息,该应用程序直到重新启动才会更新。

有没有办法实时获取心率数据?也许使用 HKObserverQuery?

到目前为止,这是我的代码:(谁设法拉动最后一次心跳)

import Foundation
import UIKit
import HealthKit


class HealthStore {

var healthStore: HKHealthStore?

init() {
    if HKHealthStore.isHealthDataAvailable(){
        healthStore = HKHealthStore()
    }
}

func requestAuthorization(completion: @escaping (Bool) -> Void){
    let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!

    guard let healthStore = self.healthStore else {return completion(false)}

    healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
    }
}

func latestHarteRate(){
    

    
    guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
        return
    }
    let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
    
    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate)
    
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
    
    let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]){(sample,result,error) in guard error == nil else{
            return
        }

    let data = result![0] as! HKQuantitySample
    let unit = HKUnit(from: "count/min")
    let latestHr = data.quantity.doubleValue(for: unit)
    print("Latest Hr\(latestHr) BPM")

    healthStore?.execute(query)
}

标签: iosswifthealthkitwatchos

解决方案


您可以使用 anHKAnchoredObjectQuery创建一个查询,该查询返回一组初始数据,然后更新该数据集。

不幸的是,您无法为 提供排序描述符HKAnchoredObjectQuery,因此如果您不想升序,则需要在收到数据后对数据进行排序。

这是我创建的模型对象,以便我可以在 SwiftUI 中进行测试。

它创建一个HKAnchoredQuery并设置一个更新处理函数。更新处理程序将 HealthKit 结果转换为我的HeartRateEntry结构(这样我就可以轻松地在 SwiftUI 列表中显示数据)。然后数组按日期降序排序。

更新功能存储newAnchor接收到的内容,以便将来仅交付更改。

在测试时,我发现在手表上运行心率应用程序,将我的测试应用程序移到后台然后切换回它会比等待新数据传输更快地触发新的心率数据。

import Foundation
import HealthKit

struct HeartRateEntry: Hashable, Identifiable {
    var heartRate: Double
    var date: Date
    var id = UUID()
}

class HeartHistoryModel: ObservableObject {
    
    @Published var heartData: [HeartRateEntry] = []
    var healthStore: HKHealthStore
    var queryAnchor: HKQueryAnchor?
    var query: HKAnchoredObjectQuery?
    
    init() {
        if HKHealthStore.isHealthDataAvailable() {
            healthStore = HKHealthStore()
        } else {
            fatalError("Health data not available")
            
        }
        
        self.requestAuthorization { authorised in
            if authorised {
                self.setupQuery()
            }
        }
    }
    
    func requestAuthorization(completion: @escaping (Bool) -> Void){
        let heartBeat = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
        
        self.healthStore.requestAuthorization(toShare: [], read: [heartBeat]) { (success, error) in completion(success)
        }
    }
    
    func setupQuery() {
        guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
            return
        }
        
        let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
        
       let predicate = HKQuery.predicateForSamples(withStart: startDate, end: .distantFuture, options: .strictEndDate)
        
        self.query = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: queryAnchor, limit: HKObjectQueryNoLimit, resultsHandler: self.updateHandler)
        
        self.query!.updateHandler = self.updateHandler
            
        healthStore.execute(self.query!)
    }
    
    func updateHandler(query: HKAnchoredObjectQuery, newSamples: [HKSample]?, deleteSamples: [HKDeletedObject]?, newAnchor: HKQueryAnchor?, error: Error?) {
        if let error = error {
            print("Health query error \(error)")
        } else {
            let unit = HKUnit(from: "count/min")
            if let newSamples = newSamples as? [HKQuantitySample], !newSamples.isEmpty {
                print("Received \(newSamples.count) new samples")
                DispatchQueue.main.async {
                    
                    var currentData = self.heartData
                    
                    currentData.append(contentsOf: newSamples.map { HeartRateEntry(heartRate: $0.quantity.doubleValue(for: unit), date: $0.startDate)
                    })
                    
                    self.heartData = currentData.sorted(by: { $0.date > $1.date })
                }
            }

            self.queryAnchor = newAnchor
        }
        
        
    }
}

推荐阅读