Ios 在触发我的完成处理程序之前,如何最好地确保我拥有所有数据?

Ios 在触发我的完成处理程序之前,如何最好地确保我拥有所有数据?,ios,swift,Ios,Swift,我不知道如何最好地获取我的健康工具包数据,特别是心率记录。我的问题是由于竞争条件(这是由于异步调用)和完成处理程序引起的 我的应用程序记录了一次训练,并最终将数据推送到远程服务器进行清理、分析。我想在会话同步请求中包含心率数据 会话由GPS和其他传感器数据组成,这些数据分为几圈 当我开始同步会话时,我会这样调用以下函数: fetchSessionLapTimes(session: session) { (storedSessionLapTime, error) in //handle t

我不知道如何最好地获取我的健康工具包数据,特别是心率记录。我的问题是由于竞争条件(这是由于异步调用)和完成处理程序引起的

我的应用程序记录了一次训练,并最终将数据推送到远程服务器进行清理、分析。我想在会话同步请求中包含心率数据

会话由GPS和其他传感器数据组成,这些数据分为几圈

当我开始同步会话时,我会这样调用以下函数:

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
})
My
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)
        }
    }
}
My
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)
}
func fetchWorkoutData(开始日期:日期,结束日期:日期,完成日期:((uu成功:Bool,u错误:error?->Void)!){
let predicate=HKQuery.predicateForSamples(具有开始:startDate,结束:endDate,选项:.strictStartDate)
let query=HKSampleQuery(sampleType:hrType,谓词:谓词,限制:Int(hkObjectQueryOnlimit),sortDescriptors:nil){
查询、结果、中的错误
guard let samples=结果为?[HKQuantitySample]其他{
完成(错误,错误)
fatalError(“获取训练数据时出错:\(错误!.localizedDescription)”;
}
如果样本数小于1{
self.debug.log(标记:“HealthStoreService”,内容:“未找到任何训练数据”)
完成(真,无)
返回
}
DispatchQueue.main.async{
样品中的样品{
self.debug.log(标记:“HealthStoreService”,内容:“\(sample.quantity.doubleValue(for:self.hrUnit)))
}
完成(真,无)
返回
}
}
healthStore.execute(查询)
}

正如您所看到的,这个函数也是异步的。如果我运行这个代码,我不会返回所有圈的心率数据在允许返回
FetchSessionAdaptimes
之前,如何确保我拥有所有圈数的心率数据?

您可以将
for
循环中的所有
fetchWorkoutData
任务添加到
调度组中。当它们全部完成时,您将收到通知,因此您可以调用函数的完成。以下是一个例子:

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)
    }
}

您可以使用
信号灯
或从属
操作队列
。您还可以使用promise kit,如或在第一次完成
fetchWorkoutData
时调用completion block。尝试使用依赖项操作您可以将当前在
for
循环中调用的任务添加到
DispatchGroup
中,并在所有任务完成时收到通知。然后启动
fetchSessionLapTimes
的完成。检查这个