使用HealthKit获取最新心率时为何始终返回0?
解决HealthKit读取心率始终返回0的问题
你的问题出在HKSampleQuery是异步执行的:当你调用healthStore?.execute(query)后,函数会立刻执行return latestHeartRateBPM,这时候查询的回调块还没开始运行,latestHeartRateBPM还是初始值0。等HealthKit真正返回心率数据、修改变量值的时候,函数早就已经返回了,所以永远拿不到正确的心率值。
要解决这个问题,你需要用**闭包(Completion Handler)**来处理异步结果,因为同步返回的方式在这里不适用。修改后的代码如下:
func getLatestHeartRate(completion: @escaping (Int?) -> Void) { guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else { completion(nil) 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) // 把limit设为1,只取最新的一条数据,没必要查所有 let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { (_, result, error) in guard error == nil, let samples = result, let latestSample = samples.first as? HKQuantitySample else { completion(nil) return } // 处理心率单位:优先尝试count/min,再用count/s转换 let bpmUnit = HKUnit.count().unitDivided(by: HKUnit.minute()) if latestSample.quantity.is(compatibleWith: bpmUnit) { let bpm = latestSample.quantity.doubleValue(for: bpmUnit) completion(Int(ceil(bpm))) } else { let bpsUnit = HKUnit.count().unitDivided(by: HKUnit.second()) let bps = latestSample.quantity.doubleValue(for: bpsUnit) completion(Int(ceil(bps * 60))) } } healthStore?.execute(query) }
使用方式:
getLatestHeartRate { heartRate in if let rate = heartRate { print("最新心率:\(rate) BPM") // 在这里处理心率值,比如更新UI } else { print("获取心率失败") } }
额外注意点:
- 把查询的
limit设为1,只获取最新的一条数据,比查所有数据更高效 - 避免强制解包
result![0],改用安全的可选绑定,防止崩溃 - 心率数据的单位可能是
count/min或count/s,需要兼容两种情况 - 确保已经向用户请求了
HKObjectType.heartRate的读取权限
内容的提问来源于stack exchange,提问作者Niels Langerak
相关产品推荐
相关产品推荐

