如何修改Swift代码实现从iPhone健康APP获取实时心率数据?
解决实时获取心率数据的方案
你的问题根源在于当前使用的HKSampleQuery是一次性查询HealthKit中已存储的历史样本,它只会返回调用时刻的最新心率数据,不会主动监听新产生的心率值。要实现实时获取,需要改用支持持续监听的HKAnchoredObjectQuery,并通过异步流来传递实时更新。
以下是修改后的实现方案:
1. 核心修改:改用HKAnchoredObjectQuery实现实时监听
我们可以封装一个返回AsyncStream<Double>的函数,让调用方能够订阅这个流,持续接收新的心率数据:
import HealthKit class HeartRateManager { private let store = HKHealthStore() private var anchor: HKQueryAnchor? private var heartRateQuery: HKAnchoredObjectQuery? // 检查并请求心率权限 func requestHeartRateAuthorization() async throws { guard HKHealthStore.isHealthDataAvailable() else { throw NSError(domain: "com.ray.app", code: 1, userInfo: [NSLocalizedDescriptionKey: "健康数据不可用"]) } let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)! try await store.requestAuthorization(toShare: [], read: [heartRateType]) } // 提供实时心率数据流 func liveHeartRateStream() -> AsyncStream<Double> { AsyncStream { continuation in let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)! let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute()) // 定义查询更新的处理闭包 let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = { [weak self] query, samples, deletedObjects, newAnchor, error in guard let self = self else { return } if let error = error { continuation.finish(throwing: error) return } // 更新锚点,避免重复处理旧样本 self.anchor = newAnchor // 处理新增的心率样本,取最新的一条返回 if let latestSample = samples?.compactMap({ $0 as? HKQuantitySample }).last { let bpm = latestSample.quantity.doubleValue(for: heartRateUnit) continuation.yield(bpm) } } // 创建锚点查询 heartRateQuery = HKAnchoredObjectQuery( type: heartRateType, predicate: nil, anchor: anchor, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler ) // 设置监听新样本的回调 heartRateQuery?.updateHandler = updateHandler guard let query = heartRateQuery else { continuation.finish(throwing: NSError(domain: "com.ray.app", code: 2, userInfo: [NSLocalizedDescriptionKey: "无法创建心率查询"])) return } store.execute(query) // 当流被取消时停止查询 continuation.onTermination = { [weak self] _ in self?.store.stop(query) self?.heartRateQuery = nil } } } }
2. 使用方式
调用方可以通过订阅这个异步流来实时获取心率更新:
let manager = HeartRateManager() // 先请求权限 do { try await manager.requestHeartRateAuthorization() // 订阅实时心率流 for try await bpm in manager.liveHeartRateStream() { print("当前心率:\(bpm) BPM") // 在这里更新UI或处理心率数据 } } catch { print("权限请求失败或出错:\(error.localizedDescription)") }
关键说明
- HKAnchoredObjectQuery:这个查询会在初始化时返回当前已有的样本,之后会自动监听HealthKit中新增的心率样本,并通过
updateHandler回调通知。 - 锚点(HKQueryAnchor):用于记录已经处理过的样本位置,确保每次只返回新产生的心率数据,避免重复发送旧值。
- AsyncStream:将查询的回调转换为异步流,符合Swift的异步/并发语法,方便调用方订阅处理。
- 停止监听:通过
continuation.onTermination在流取消时停止查询,避免内存泄漏。
额外注意事项
- 确保你的App在
Info.plist中添加了NSHealthShareUsageDescription和NSHealthUpdateUsageDescription(虽然这里只需要读取权限,但仍需配置)。 - Apple Watch上的心率数据需要确保手表处于活跃状态(比如佩戴在手腕上),HealthKit才会持续记录新的心率样本。
内容的提问来源于stack exchange,提问作者Game_Time
相关产品推荐
相关产品推荐

