【问题标题】:SwiftUI and Firestore - logic for reading multiple documents from different collections at startupSwiftUI 和 Firestore - 启动时从不同集合中读取多个文档的逻辑
【发布时间】:2020-12-09 15:46:25
【问题描述】:

我在我的 SwiftUI 应用项目中使用 Firebase Firestore。在应用程序启动时,我想从各种集合中读取多个文档。

假设我有集合 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 中的第一个文档。

当然,必须有一个更好的逻辑来在我的应用程序启动时从数据库中加载我的数据,它可以在运行时正确处理每个文档更新,并且没有不必要的双重读取。

有人有更好的方法吗?谢谢

【问题讨论】:

    标签: firebase google-cloud-firestore swiftui logic read-data


    【解决方案1】:

    所以这是一个可能的解决方案。您必须根据自己的需要对其进行修改,但由于您为您的问题提供的代码很少甚至没有,因此它必须这样做。

    1. 从 A 获取数据
    2. 当数据到达时,使用它来确定从 B 获取哪些数据。 为此使用完成处理程序。其他解决方案可以是但不限于(Dispatcher 等)。
    3. 使用从 B 提供的数据确定要从 C 获取哪些数据。 等待它的过程与 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)
                }
            }
            
        }
        
    }
    

    【讨论】:

    • 1.我正在使用 snapshotListeners ("sL") 而不是一次读取数据(我可能没有强调这一点) 2. 如果我正确理解您的方法,它仍然无法解决以下问题:我必须阅读B 中两个文档的数据,所以我为每个文档添加了一个 sL。只有在读取了 B 中所有需要的文档(本例中为:两个)后,程序才继续从 C 中读取数据(readC)。现在(这是问题所在),来自 B 中的一个文档的数据被更改,这触发了它的 sL。此数据仅更新到 B 中的一个文档是否会导致新的 readC 调用(根据需要)?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-24
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    相关资源
    最近更新 更多