You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 15:17:46