iOS Swift HealthKit:HKAnchoredObjectQuery获取心跳序列样本失败求助
解决HKAnchoredObjectQuery获取心跳序列样本崩溃并实现持续更新的问题
崩溃原因分析
你遇到的崩溃核心是谓词与查询类型不兼容:HKQuery.predicateForWorkouts(with: .cycling)是专门为HKWorkout类型设计的过滤条件,不能直接用于HKHeartbeatSeriesSample类型的查询。HealthKit会严格校验查询类型与谓词的匹配性,因此抛出了数据类型不允许的异常。
解决方案
要获取属于骑行Workout的心跳序列样本,需要通过心跳样本与Workout的关联关系构建合法谓词,而非直接复用Workout类型的谓词。同时HKAnchoredObjectQuery完全支持HKHeartbeatSeriesSample类型,配置正确后即可实现持续更新。
修改后的完整代码
func getBeatSamples(startDate: Date) { let heartbeatSeriesType = HKSeriesType.heartbeat() var myAnchor: HKQueryAnchor? = HKQueryAnchor(fromValue: 0) // 1. 先获取所有骑行类型的Workout,用于关联心跳样本 let workoutPredicate = HKQuery.predicateForWorkouts(with: .cycling) let workoutQuery = HKSampleQuery(sampleType: HKObjectType.workoutType(), predicate: workoutPredicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { [weak self] _, samples, error in guard let self = self, error == nil, let workouts = samples as? [HKWorkout] else { return } // 2. 构建心跳样本属于这些骑行Workout的合法谓词 let workoutUUIDs = workouts.map { $0.uuid } let heartbeatPredicate = HKQuery.predicateForObjects(from: workoutUUIDs) let timePredicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: []) let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [timePredicate, heartbeatPredicate]) // 定义更新处理逻辑 let updateHandler: (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = { _, samplesOrNil, _, newAnchor, errorOrNil in guard errorOrNil == nil, let samples = samplesOrNil as? [HKHeartbeatSeriesSample] else { return } // 更新锚点,用于后续增量查询 myAnchor = newAnchor // 遍历所有新的心跳序列样本,解析逐次心跳数据 for heartbeatSeries in samples { let beatQuery = HKHeartbeatSeriesQuery(heartbeatSeries: heartbeatSeries) { _, timeSinceSeriesStart, _, done, error in guard error == nil else { done(true) return } // 处理单条心跳的计时数据 print("心跳间隔:\(timeSinceSeriesStart) 秒") done(false) // 继续解析后续心跳 } self.healthStore.execute(beatQuery) } // 主线程更新UI DispatchQueue.main.async { // 在这里更新你的UI组件 } } // 创建并执行锚定查询 let query = HKAnchoredObjectQuery(type: heartbeatSeriesType, predicate: compoundPredicate, anchor: myAnchor, limit: HKObjectQueryNoLimit, resultsHandler: updateHandler) query.updateHandler = updateHandler self.healthStore.execute(query) } healthStore.execute(workoutQuery) }
关键修改点
- 先通过
HKSampleQuery获取所有骑行类型的Workout,提取它们的UUID - 使用
HKQuery.predicateForObjects(from: workoutUUIDs)构建心跳样本与骑行Workout的关联谓词,替代原来的错误谓词 - 遍历所有返回的心跳序列样本,不再仅处理第一个样本
- 增加
done(false)确保HKHeartbeatSeriesQuery能继续解析后续心跳数据(done(true)会终止查询)
权限配置确认
你的typesToRead已经正确包含了HKSeriesType.heartbeat(),无需额外修改:
let typesToRead: Set<HKObjectType> = [ HKQuantityType(.heartRate), HKSeriesType.heartbeat(), HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!, HKQuantityType(.activeEnergyBurned), HKQuantityType(.distanceWalkingRunning), HKQuantityType(.cyclingSpeed), HKQuantityType(.cyclingPower), HKQuantityType(.cyclingCadence), HKQuantityType(.distanceCycling), HKQuantityType(.dietaryWater), HKQuantityType.workoutType(), HKObjectType.activitySummaryType() ]
内容的提问来源于stack exchange,提问作者davidmct
相关产品推荐
相关产品推荐

