Swift4。在继续执行之前,等待HealthKit HKQuery的异步结果

时间:2018-10-31 20:19:04

标签: swift asynchronous grand-central-dispatch health-kit completionhandler

问题是如何在允许执行继续之前等待HealthKit上的异步查询返回结果。返回的数据对于进一步执行至关重要。

我知道这个问题已经被问到/解决了很多次,我已经阅读了很多帖子,但是我尝试了完成处理程序,Dispatch同步和Dispatch组,并且无法提出可行的实现方案。

使用完成处理程序 每个Wait for completion handler to finish - Swift

这将调用一种方法来运行HealthKit查询:

func readHK() {

    var block: Bool = false

    hk.findLastBloodGlucoseInHealthKit(completion: { (result) -> Void in
        block = true

        if !(result) {
            print("Problem with HK data")
        }

        else {
            print ("Got HK data OK")
        }

    })

    while !(block) {
    }

    // now move on to the next thing ...

}

这确实有效。在概念上使用“ block”变量将执行保留为挂起的回调似乎与阻止信号量没有什么不同,但是这确实很丑陋,并且如果由于某种原因而没有返回完成,就会遇到麻烦。有更好的方法吗?

使用调度组

如果我将调度组放在调用功能级别:

调用功能:

func readHK() {

    var block: Bool = false
    dispatchGroup.enter()

    hk.findLastBloodGlucoseInHealthKit(dg: dispatchGroup)
    print ("Back from readHK")

    dispatchGroup.notify(queue: .main) {
        print("Function complete")
        block = true
    }

    while !(block){   
    }
}

接收功能:

func findLastBloodGlucoseInHealthKit(dg: DispatchGroup) {

        print ("Read last HK glucose")

        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let query = HKSampleQuery(sampleType: glucoseQuantity!, predicate: nil, limit: 10, sortDescriptors: [sortDescriptor]) { (query, results, error) in
            // .... other stuff
            dg.leave()

完成执行OK,但是从不调用.notify方法,因此block变量从不更新,程序挂起,也不会从while语句退出。

在目标函数中放入调度组,但在调用级别保留.notify:

func readHK() {

    var done: Bool = false

    hk.findLastBloodGlucoseInHealthKit()
    print ("Back from readHK")

    hk.dispatchGroup.notify(queue: .main) {
        print("done function")
        done = true
    }

    while !(done) {
    }

}

同一问题。

使用调度

文档和其他S.O帖子说:“如果要等待块完成,请使用sync()方法。

但是“完整”是什么意思?似乎并不意味着完成该功能并获得以后的异步完成。例如,在完成返回之前,以下内容不保留执行:

func readHK() {

    DispatchQueue.global(qos: .background).sync {
        hk.findLastBloodGlucoseInHealthKit()
    }

    print ("Back from readHK")

}

谢谢您的帮助。

1 个答案:

答案 0 :(得分:0)

是的,请不要与事物的异步性质作斗争。通过制作效率低下的应用(计时器和其他延迟),或者通过实现自己的阻止功能为难以诊断的错误创造机会,您几乎总是会迷失。

我离Swift / iOS专家还很远,但是看来最好的选择是使用Grand Central Dispatch或第三方库之一来管理异步工作。例如,看看PromiseKit,尽管我还没有看到像JavaScript的bluebird这样的Swift Promises / Futures库那么好。

您可以使用DispatchGroup跟踪查询的完成处理程序。设置查询时,调用“ enter”方法,并在结果处理程序的末尾(而不是在设置或执行查询之后)调用“ leave”。即使查询完成并出现错误,也请确保退出。我不确定您为什么会遇到麻烦,因为这在我的应用程序中效果很好。我认为,诀窍是确保无论出什么问题,您都始终“离开”调度组。

如果愿意,可以在DispatchQueue中设置屏障任务-仅在队列中所有较早的任务完成后才执行-而不使用DispatchGroup。为此,您可以在DispatchWorkItem中添加正确的options