从 Health App 实时获取新的心率数据?

Getting new heart rate data live from Health App?

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

有没有办法实时获取心率数据?也许与 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)
}

您可以使用 HKAnchoredObjectQuery 创建一个查询,该查询 returns 初始数据集,然后更新数据集。

很遗憾,您无法为 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: [=10=].quantity.doubleValue(for: unit), date: [=10=].startDate)
                    })
                    
                    self.heartData = currentData.sorted(by: { [=10=].date > .date })
                }
            }

            self.queryAnchor = newAnchor
        }
        
        
    }
}