Как получить прогресс асинхронного чтения данных Firebase?

# #ios #swift #firebase #google-cloud-firestore

Вопрос:

У меня есть код, который считывает данные из Firebase на пользовательском экране загрузки, который я хочу перейти только после того, как все данные в коллекции будут прочитаны (я заранее знаю, что для чтения будет не более 10 или 15 записей данных, и я проверяю, есть ли у пользователя подключение к Интернету). У меня есть анимация загрузки, которую я хотел бы реализовать, которая запускается вызовом activityIndicatorView.startAnimating() и останавливается вызовом activityIndicatorView.stopAnimating(). Я не уверен, где разместить их или функцию perform segue по отношению к функции извлечения данных. Любая помощь будет признательна!

 let db = Firestore.firestore()
            
    db.collection("Packages").getDocuments{(snapshot, error) in
        
        if error != nil{
            // DB error
        } else{
            
            for doc in snapshot!.documents{
                
                self.packageIDS.append(doc.documentID)
                self.packageNames.append(doc.get("title") as! String)
                self.packageIMGIDS.append(doc.get("imgID") as! String)
                self.packageRadii.append(doc.get("radius") as! String)

            }
            
        }
        
    }
 

Комментарии:

1. Используйте блок завершения.

2. Я не использовал это раньше, не могли бы вы уточнить, пожалуйста?

3. Firebase асинхронна — это означает, что требуется время для поступления данных с сервера Firebase. Функции Firebase используют замыкания — это раздел в скобках {} после вызова. Данные Firebase действительны в рамках этого закрытия , поэтому, если вы хотите принять меры после поступления данных Firebase, это самое подходящее место для этого. Имейте в виду, что FIrebase невероятно быстра — даже с большими наборами данных, поэтому вам, вероятно, не нужен этот индикатор прогресса, так как он будет отображаться недостаточно долго, чтобы что-то изменить. (вообще говоря)

4. Понял, спасибо за понимание!

Ответ №1:

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

Чтение начинается, когда вы звоните getDocuments .

Чтение завершается после завершения for цикла getDocuments завершения.

Так:

 let db = Firestore.firestore()

activityIndicatorView.startAnimating()
            
db.collection("Packages").getDocuments{(snapshot, error) in
        
    if error != nil{
            // DB error
    } else {
            
        for doc in snapshot!.documents{
            self.packageIDS.append(doc.documentID)
            self.packageNames.append(doc.get("title") as! String)
            self.packageIMGIDS.append(doc.get("imgID") as! String)
            self.packageRadii.append(doc.get("radius") as! String)
        }
    }
    DispatchQueue.main.async {
        activityIndicatorView.stopAnimating()
    }
}
 

С точки зрения стиля, наличие нескольких массивов с ассоциированными данными-это своего рода запах кода. Скорее вам следует создать a struct с соответствующими свойствами и создать единый массив экземпляров этой структуры.

Вам также следует избегать принудительного разворачивания.

 struct PackageInfo {
     let id: String
     let name: String
     let imageId: String
     let radius: String
}

...


var packages:[PackageInfo] = []

...   
    db.collection("Packages").getDocuments{(snapshot, error) in
        
    if error != nil{
            // DB error
    } else if let documents = snapshot?.documents {

        self.packages = documents.compactMap { doc in
            if let title = doc.get("title") as? String,
               let imageId = doc.get("imgID") as? String,
               let radius = doc.get("radius") as? String {
                   return PackageInfo(id: doc.documentID, name: title, imageId: imageId, radius: radius)
            } else {
               return nil
            }
        }
    }
        
 

Комментарии:

1. Большое вам спасибо, цените дополнительные советы! Два вопроса: 1. Поскольку функции данных firebase являются асинхронными, как DispatchQueue.main.async «обходит» это? 2. Как я могу получить доступ к объекту/структуре PackageInfo, используя указанный идентификатор (идентификатор: doc.documentId)? {т. Е. Если я хочу изменить некоторые поля в структуре для определенного идентификатора документа [обновления в реальном времени]}

2. Основная асинхронность очереди отправки не обходит асинхронную выборку. Это просто гарантирует, что обновление пользовательского интерфейса (остановка индикатора активности) произойдет в основной очереди, поскольку все обновления пользовательского интерфейса должны выполняться в основной очереди. Все в закрытии выполняется после завершения выборки

3. Ну, у вас есть идентификатор документа, так что вы можете либо получить его, либо применить обновления. Другой способ, которым вы могли бы это сделать, — сохранить весь документ в своей структуре

4. Это имеет смысл в очереди отправки, спасибо. И о структуре пакета, верно, но как мне получить пакет с этим идентификатором? Нужно ли мне вручную искать структуру и отмечать индекс, в котором совпадает идентификатор пакета, и использовать этот индекс, например, для удаления структуры пакета из массива? Я знаю, что с массивами есть .indexOf() и .remove(at:), существует ли аналогичное предыдущее для массива структур, где каждая структура связана с идентификатором…?

5. Я не знаю, как вы используете массив. Если вы показываете данные в виде таблицы, то обычно знаете индекс, с которым работаете. В противном случае вы можете выполнить поиск по массиву (не очень эффективно) или использовать словарь id:package.

Ответ №2:

В рамках одной операции чтения нет отчетов о ходе выполнения, либо она находится в ожидании, либо завершена.

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

Комментарии:

1. Как я могу проверить, завершена ли операция чтения (это помогло бы мне)?

2. Операция чтения завершается при вызове обратного вызова. Так что, если все, что вы хотите сделать, это скрыть анимацию, это было бы хорошим местом для этого.