Swift 4. Дождитесь асинхронного результата HealthKit HKQuery, прежде чем продолжить выполнение - PullRequest
0 голосов
/ 31 октября 2018

Проблема заключается в том, как дождаться, пока асинхронный запрос к HealthKit вернет результат ДО того, как выполнение будет продолжено. Возвращенные данные важны для дальнейшего выполнения.

Я знаю, что об этом спрашивали / решали много раз, и я прочитал много постов, однако я пробовал обработчики завершения, Диспетчерскую синхронизацию и Группы рассылки и не смог найти реализацию, которая работает.

Использование обработчика завершения за Дождитесь завершения обработчика завершения - 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» для удержания выполнения в ожидании обратного вызова в концепции, похоже, не сильно отличается от блокировки семафоров, но это действительно уродливо и вызывает проблемы, если завершение не возвращается по какой-либо причине. Есть ли лучший способ?

Использование групп рассылки

Если я поместил Dispatch Group на уровень вызывающей функции:

Функция вызова:

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

Завершение выполняется ОК, но метод .notify никогда не вызывается, поэтому переменная блока никогда не обновляется, программа зависает и никогда не выходит из оператора 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 голосов
/ 11 декабря 2018

Да, пожалуйста, не боритесь с асинхронной природой вещей. Вы почти всегда проиграете, либо создав неэффективное приложение (таймеры и другие задержки), либо создав возможности для трудно диагностируемых ошибок за счет реализации собственных функций блокировки.

Я далеко не эксперт по Swift / iOS, но, похоже, ваши лучшие альтернативы - использовать Grand Central Dispatch или одну из сторонних библиотек для управления асинхронной работой. Посмотрите, к примеру, PromiseKit , хотя я не видел такой хорошей библиотеки Swift Promises / Futures, как bluebird .

в JavaScript.

Вы можете использовать DispatchGroup, чтобы отслеживать обработчик завершения для запросов. Вызывайте метод «ввод» при настройке запроса и «уход» в конце обработчика результатов, а не после того, как запрос был настроен или выполнен. Убедитесь, что вы выходите, даже если запрос завершен с ошибкой. Я не уверен, почему у вас возникли проблемы, потому что это прекрасно работает в моем приложении. Я думаю, что хитрость заключается в том, чтобы вы всегда "покидали ()" группу рассылки независимо от того, что пошло не так.

Если вы предпочитаете, вы можете установить барьерную задачу в DispatchQueue - она ​​будет выполняться только после завершения всех предыдущих задач в очереди - вместо использования DispatchGroup. Это делается путем добавления правильных параметров к DispatchWorkItem.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...