我对如何最好地获取我的 HealthKit 数据感到困惑,尤其是心率记录。我的问题是由于竞争条件引起的,这是由于异步调用和完成处理程序引起的。
我的应用程序会记录一次锻炼,并最终将该数据推送到远程服务器以进行清理和分析。我想在 session 同步请求中包含心率数据。
一次训练由 GPS 和其他分成几圈的传感器数据组成。
当我开始同步 session 时,我会像这样调用以下函数:
fetchSessionLapTimes(session: session) { (storedSessionLapTime, error) in
//handle the received lap time data
//At this point, I expect my storedSessionLapTime variable to
//contain my session lap times, and heart rate data
})
我的 fetchSessionLapTimes
函数定义如下:
func fetchSessionLapTimes(session: Session, withCompletion complete: ((_ storedSessionLapTime: [SessionLapTime], _ error: Error?) -> Void)!) {
var storedSessionLapTime = [SessionLapTime]()
managedObjectContext.performAndWait {
//get session lap times from Core Data
//this code works fine, as expected and populates
//storedSessionLapTime
}
//now this is where my problem is. At this point I want to extract
//heart rate data for each lap time
let healthStoreService = HealthStoreService()
for sessionLapTime in storedSessionLapTime {
let start = Date(timeIntervalSince1970: sessionLapTime.lapStartDate)
let end = Date(timeIntervalSince1970: sessionLapTime.lapEndDate)
healthStoreService.fetchWorkoutData(startDate: start, endDate: end) { (success, error) in
complete(storedSessionLapTime, nil)
}
}
}
我的fetchSessionLapTimes
函数定义如下:
func fetchWorkoutData(startDate: Date, endDate: Date, completion: ((_ success: Bool, _ error: Error?) -> Void)!) {
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
let query = HKSampleQuery(sampleType: hrType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: nil) {
query, results, error in
guard let samples = results as? [HKQuantitySample] else {
completion(false, error)
fatalError("Error fetching workout data: \(error!.localizedDescription)");
}
if samples.count < 1 {
self.debug.log(tag: "HealthStoreService", content: "No workout data found")
completion(true, nil)
return
}
DispatchQueue.main.async {
for sample in samples {
self.debug.log(tag: "HealthStoreService", content: "\(sample.quantity.doubleValue(for: self.hrUnit))")
}
completion(true, nil)
return
}
}
healthStore.execute(query)
}
如您所见,此函数也是异步的。如果我运行这段代码,我不会取回所有圈数的心率数据。 在允许 fetchSessionLapTimes
返回之前,如何确保我拥有所有圈数的心率数据?
最佳答案
您可以将 for
循环中的所有 fetchWorkoutData
任务添加到 DispatchGroup
。当所有这些都完成时,您会收到通知,因此您可以调用完成函数。这是一个例子:
func fetchSessionLapTimes(session: Session, withCompletion complete: ((_ storedSessionLapTime: [SessionLapTime], _ error: Error?) -> Void)!) {
var storedSessionLapTime = [SessionLapTime]()
managedObjectContext.performAndWait {
//
}
// Here you were putting async calls to a for loop and couldn't tell when all were completed
// DispatchGroup is made exactly to handle this
let dispatchGroup = DispatchGroup()
let healthStoreService = HealthStoreService()
for sessionLapTime in storedSessionLapTime {
let start = Date(timeIntervalSince1970: sessionLapTime.lapStartDate)
let end = Date(timeIntervalSince1970: sessionLapTime.lapEndDate)
// Adding the task to group
dispatchGroup.enter()
healthStoreService.fetchWorkoutData(startDate: start, endDate: end) { (success, error) in
// notifying when this particular task finishes
dispatchGroup.leave()
}
}
// completion is called only when all of the DispatchGroup tasks are finished
dispatchGroup.notify(queue: .main) {
// call completion here because all tasks completed
complete(storedSessionLapTime, nil)
}
}
关于ios - 如何最好地确保在触发完成处理程序之前我拥有所有数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51834672/