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

标签 ios swift

我对如何最好地获取我的 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/

相关文章:

ios - DDMathParser (iOS 5) : How to set radians versus degrees?

ios - 在Swift中的通用类中切换通用类型

ios - ALAssetsLibrary.enumerateGroupsWithTypes Swift 中的第一个参数

ios - 为什么 DispatchQueue.main.async 会对单元自动调整大小产生影响?

ios - 在许多 View Controller 上使用委托(delegate)和协议(protocol)快速更改主题

iphone - 在 iPad 上运行但在模拟器中没有显示图像

ios - swift 3/ Alamofire 4 : Fetching data with the ID on a TableView

iOS 子类化自定义类

ios - swift 2.0 - "Argument passed to call that takes no arguments"

swift - 如何快速查找 UIImage 的名称/标题?