Как дождаться асинхронного вызова Firebase в основном потоке (Swift) - PullRequest
1 голос
/ 28 мая 2020

Фон:

Я использую библиотеку под названием KolodaView, которая генерирует карты в стиле Tinder для приложений Swift. Карты отображаются во время функции viewDidLoad() моего контроллера представления после назначения делегата. Я написал функцию делегата viewForCardAt(), чтобы для каждой отображаемой карты выполнялся вызов getPics(), который устанавливает глобальную переменную imageResult: UIImageView, содержащую UIImageView (Примечание: UIView, возвращаемое из viewForCardAt() - это фактическое UIView, которое будет помещено на карту).

Проблема:

getPics() вызывает вызов базы (storageRef.getData()) и, как следствие, асинхронный. Поскольку viewForCardAt() должен что-то возвращать (в процессе генерации карточек) и выполняется в основном потоке, он не может ждать или получать «уведомление», когда функция getPics() завершается. Невозможность ожидания приводит к тому, что визуализированные карты не имеют изображения, поскольку getPics() не завершил выборку данных до того, как viewForCardAt() вернет свои UIView.

Что я пробовал:

Использование ожидания группы отправки, но поскольку оба этих вызова происходят в основном потоке, добавление оператора ожидания либо в функции Koloda viewForCardAt(), либо даже в функции getPics() приводит к зависанию всего приложения. Я думал о том, чтобы каким-то образом поместить getPics в другой асинхронный поток, но тогда viewForCardAt() все равно придется ждать завершения getPics(), поскольку он не может вернуться, пока getPics() не «вернет свое значение» (присвоит его значение к глобальной ссылке).

Соответствующий код (абстрагированный)


myViewController() {

    var imageResult: UIImageView

    override func viewDidLoad() {
        self.kolodaView.delegate = self
    }

    .
    .
    .

    func getPics() {
        grabImageDispatchGroup.enter()
        let storage = Storage.storage
        let storageRef = storage.reference(forURL: photoLink)
        //the asynch firebase call
        storageRef.getData(....) { (data, error) in
            //the actual result of the query being stored
            //for the Koloda card to use on the current card
            self.imageResult.image = UIImage(data: data)
            grabImageDispatchGroup.leave()
        }
    }

    //run for element in a specified 'user' array ("dataSource" stuff not shown for brevity) 
    func Koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {

        var cardView = UIView()
        getPics()
        grabImageDispatchGroup.notify() { //this cannot work since we must return a value
            cardView.addSubview(imageResult)
            return cardView
        }

        //invalid: 'this function must return something'
    }

}

1 Ответ

1 голос
/ 28 мая 2020

Использовать обработчик завершения вместо return

func Koloda(_ koloda: KolodaView, viewForCardAt index: Int , completion:@escaping (UIView) -> Void )  {

    var cardView = UIView()
    getPics()
    grabImageDispatchGroup.notify() { //this cannot work since we must return a value
        cardView.addSubview(imageResult)
        return completion( cardView)
    }

    //invalid: 'this function must return something'
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...