Возникли проблемы с использованием DispatchQueue для ожидания возврата данных из Cloud Firestore в приложении iOS с использованием Swift - PullRequest
0 голосов
/ 11 февраля 2020

Я пытаюсь использовать DispatchQueue, чтобы заставить мой код ждать, пока запрос извлечет нужные мне результаты из Cloud Firestore, прежде чем он продолжит выполняться, но просто не смог заставить его работать. В приведенном ниже коде я пытаюсь заставить его ждать, пока данные не будут извлечены и сохранены в zoneMarkerArray, а затем распечатать результат.

Я пронумеровал каждую напечатанную строку в том порядке, в котором я хочу, чтобы это происходило, и, как вы увидите на выходе, он не ждет результата Firestore, прежде чем двигаться дальше.

Вот мой код:

let zones = self.db.collection("zones")

let zonesQuery = zones.whereField("start", isGreaterThan: lowerLimit).whereField("start", isLessThan: upperLimit)

print("1. zones Query has been defined")

//pass zonesQuery query to getZoneMarkers function to retrieve the zone markers from Firestore      
getZoneMarkers(zonesQuery)

print("6. Now returned from getZoneMarkers")


func getZoneMarkers(_ zonesQuery: Query) -> ([Double]) {
    print("2. Entered getZoneMarkers function")
    DispatchQueue.global(qos: .userInteractive).async {         

        zonesQuery.getDocuments() { (snapshot, error) in

            if let error = error {
            print("Error getting zone markers: \(error)")
            } else {

                print("3. Successfully Retrieved the zone markers")
            var result: Double = 0.0

                for document in snapshot!.documents {

                    print("Retrieved zone marker is \(document["start"]!)")
                    self.zoneMarkerArray.append(document["start"]! as! Double)
                    print("4. Looping over zone marker results")

                }
            }
        }

        DispatchQueue.main.async { 
      //I want this the printCompleted function to print the result AFTER the results have been retrieved  
            self.printCompleted()

        }
    }   

    return self.zoneMarkerArray

}


func printCompleted() {
    print("5. Looping now completed. Result was \(zoneMarkerArray)")
}

А вот вывод, который выводится:

  1. Зоны Запрос был определен
  2. Введена функция getZoneMarkers
  3. Теперь возвращается из getZoneMarkers
  4. Цикл теперь завершен. Результат был [0.0]
  5. Успешно извлечены маркеры зоны
  6. Цикл по результатам маркера зоны
  7. Цикл по результатам маркера зоны Получен маркер зоны 12.0
  8. Цикл результаты маркера над зоной

Спасибо за помощь!

РЕДАКТИРОВАТЬ: В случае, если кто-то еще сталкивается с этим, вот рабочий код, который я собрал в конце на основе обратная связь, которую я получил. Пожалуйста, не стесняйтесь критиковать, если вы видите, как это может быть улучшено:

let zones = self.db.collection("zones")

let zonesQuery = zones.whereField("start", isGreaterThan: lowerLimit).whereField("start", isLessThan: upperLimit)


print("1. zones Query has been defined")

//pass zonesQuery query to getZoneMarkers function to retrieve the zone markers from Firestore      
getZoneMarkers(zonesQuery)

func getZoneMarkers(_ zonesQuery: (Query)) {
    print("2. Entered getZoneMarkers function")
  zoneMarkerArray.removeAll()

    zonesQuery.getDocuments(completion: { (snapshot, error) in
        if let error = error {
            print("Error getting zone markers: \(error)")
            return
        }

        guard let docs = snapshot?.documents else { return }

        print("3. Successfully Retrieved the zone markers")


        for document in docs {

            self.zoneMarkerArray.append(document["start"]! as! Double)
            print("4. Looping over zone marker results")

        }

        self.completion(zoneMarkerArray: self.zoneMarkerArray)

    })
}


func completion(zoneMarkerArray: [Double]) {
    print("5. Looping now completed. Result was \(zoneMarkerArray)")   

}

Ответы [ 2 ]

0 голосов
/ 12 февраля 2020

Из вопроса не похоже, что нужны какие-либо DispatchQueue. Firestore асинхронный, поэтому данные действительны только внутри замыканий, следующих за функцией firebase. Кроме того, вызовы пользовательского интерфейса обрабатываются в основном потоке, в то время как работа в сети происходит в фоновом потоке.

Если вы хотите дождаться загрузки всех данных из Firestore, это будет сделано в закрытии после Firestore вызов. Например, предположим, что у нас есть коллекция зон с документами, в которых хранятся индикаторы запуска и остановки

zones
   zone_0
      start: 1
      stop:  3
   zone_1
      start: 7
      stop:  9

В этом примере мы будем хранить старт и остановку для каждой зоны в массиве классов кортежей

var tupleArray = [(Int, Int)]()

и код для чтения в зонах, заполните tupleArray, а затем выполните «следующий шаг» - в этом случае напечатайте их.

func readZoneMarkers() {
    let zonesQuery = self.db.collection("zones")
    zonesQuery.getDocuments(completion: { documentSnapshot, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }

        guard let docs = documentSnapshot?.documents else { return }

        for doc in docs {
            let start = doc.get("start") as? Int ?? 0
            let end = doc.get("end") as? Int ?? 0
            let t = (start, end)
            self.tupleArray.append(t)
        }

        //reload your tableView or collectionView here,
        //    or proceed to whatever the next step is
        self.tupleArray.forEach { print( $0.0, $0.1) }
    })
}

и вывод

1 3
7 9

Из-за асинхронной природы Firebase вы не можете «вернуться» из замыкания, но вы можете использовать обработчик завершения, если необходимо передать данные «назад» из замыкания.

0 голосов
/ 11 февраля 2020

Может быть, это может помочь вам. У меня много пользователей, оно добавляется к моей модели и может проверять, когда у меня есть все данные и go с моим кодом:

func allUser (completion: @escaping ([UserModel]) -> Void) {

let dispatchGroup = DispatchGroup()
var model = [UserModel]()

let db = Firestore.firestore()
let docRef = db.collection("users")
dispatchGroup.enter()
docRef.getDocuments { (querySnapshot, err) in

    for document in querySnapshot!.documents {
        print("disp enter")
        let dic = document.data()
        model.append(UserModel(dictionary: dic))
    }
     dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
    completion(model)
    print("completion")
}

}

...