SwiftUI и Firestore - логика для чтения нескольких документов из разных коллекций при запуске

Я использую Firebase Firestore в своем проекте приложения SwiftUI. В начале приложения я хочу прочитать несколько документов из разных коллекций.

Скажем, у меня есть коллекция A (1 документ), B (2 документа) и C (4 документа). Данные в документах из A и B определяют, какие документы из C я должен прочитать.

В настоящее время у меня есть класс DatabaseHelper, который имеет функцию readCollection для каждой коллекции (каждая из этих функций readCollection вызывает функции readDocument для создания слушателя для каждого необходимого документа). Каждая функция readCollection, а также каждая функция readDocument имеет обработчик завершения. В начале приложения я начинаю с чтения коллекции A. После завершения чтения начинается чтение B и так далее:

func listen(completion: @escaping () -> ()) {
  readA() {
    readB() {
      readC() {
        completion()
      }
    }
  }
}

Мое приложение ожидает завершения функции прослушивания перед отображением каких-либо данных.

Этот подход «вроде как работает», ОДНАКО, он не кажется оптимальным. Например, если я читаю A, то я читаю оба документа из B, обработчик завершения из B вызывается дважды, что приводит к двум вызовам readC (), если я не подсчитываю чтение документа для B и вызываю завершение только после последнее чтение. ТО проблема в том, что только обновление второго документа в B запускает обработчик завершения и перезагружает документы из C, но не обновление первого документа в B.

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

Есть ли у кого-нибудь подход лучше? Спасибо


person ediheld    schedule 20.08.2020    source источник


Ответы (1)


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

  1. Получите свои данные из A
  2. Когда поступят эти данные, используйте их, чтобы определить, какие данные получить от B. Используйте для этого обработчик завершения. Другие решения могут быть и не ограничиваться (Диспетчер и т. Д.).
  3. Определите, какие данные получить от C с данными, полученными от B. Та же процедура, что и в Nr.2, чтобы дождаться их.

Это всего лишь базовое представление о возможной процедуре решения вашей проблемы. Это слишком упрощенно, но должно помочь вам понять:

class DatabaseHelper: ObservableObject {
    
    @Published var myDataA: Array<String> = []
    @Published var myDataB: Array<String> = []
    @Published var myDataC: Array<String> = []
    
    init() {
        self.getData(dataBaseACollectionName: "dataBase_A")
    }
    
    // Get A Data
    private func getData(dataBaseACollectionName: String) {
        self.dataBaseCollectionGetDocuments(collectionName: dataBaseACollectionName) { isSucceededA in
            if !isSucceededA.isEmpty {
                self.myDataA = isSucceededA
                
                // Get first "documentID from A to determine what in B I have to read
                self.getSpecificDocumentFromDataBase(collectionName: self.myDataA.first ?? "randomCollection", documentID: self.myDataA.first ?? "randomDocument") { isSucceededB in
                    if !isSucceededB.isEmpty {
                        self.myDataB = isSucceededB
                        
                        // Get first "documentID from B to determine what in C I have to read
                        self.getSpecificDocumentFromDataBase(collectionName: self.myDataB.first ?? "randomCollection", documentID: self.myDataB.first ?? "randomDocument") { isSucceededC in
                            if !isSucceededC.isEmpty {
                                self.myDataC = isSucceededC
                                // done
                            }
                        } // C
                        
                    }
                } // B
                
            } // A
        }
    }
    
    // I made just one wrapper function for the DB call as all myData Arrays are from the same type (containing Strings)
    private func dataBaseCollectionGetDocuments(collectionName: String ,completing: @escaping (Array<String>) -> Void) {
        
        if !collectionName.isEmpty {
            var dataArray: Array<String> = []
            let group = DispatchGroup()
            group.enter() // initial enter
            
            Firestore.firestore().collection(collectionName).getDocuments() { (documents, error) in
                if error != nil {
                    print("Error getting amount of recipes in last seen")
                } else {
                    for doc in documents!.documents {
                        group.enter()
                        dataArray.append(doc.documentID) // Just appending ID as it is a String
                        group.leave()
                    }
                }
                group.leave() // Initial leave
            }
            
            group.notify(queue: DispatchQueue.global(qos: .background)) {
                completing(dataArray)
            }
        }
        
    }
    
    // For Specific documents
    private func getSpecificDocumentFromDataBase(collectionName: String, documentID: String ,completing: @escaping (Array<String>) -> Void) {
        
        if !collectionName.isEmpty && !documentID.isEmpty {
            var dataArray: Array<String> = []
            let group = DispatchGroup()
            group.enter() // initial enter
            
            let docRef = Firestore.firestore().collection(collectionName).document(documentID)
            docRef.getDocument() { (document, error) in
                group.enter()
                if error != nil {
                    print("Error getting amount of recipes in last seen")
                } else {
                    dataArray.append(document!.documentID) // Just appending ID as it is a String
                    group.leave()
                }
                group.leave() // Initial leave
            }
            
            group.notify(queue: DispatchQueue.global(qos: .background)) {
                completing(dataArray)
            }
        }
        
    }
    
}
person Simon    schedule 20.08.2020
comment
1. Я использую snapshotListeners (sL) вместо однократного чтения данных (я, вероятно, не подчеркивал это) 2. Если я правильно понимаю ваш подход, он все равно не дает решения следующей проблемы: я должен прочитать данные из обоих документов в B, поэтому я добавляю sL к каждому из документов. Только после прочтения всех необходимых документов в B (в данном случае - двух) программа продолжает считывать данные из C (readC). ТЕПЕРЬ (вот в чем проблема) данные из одного документа в B изменяются, что запускает его sL. Эти данные обновляются только до одного документа в B, что приводит к новому вызову readC (по желанию)? - person ediheld; 24.08.2020