如何在 Swift 中重构重复的 Firestore 文档 ID?

Posted

技术标签:

【中文标题】如何在 Swift 中重构重复的 Firestore 文档 ID?【英文标题】:How to refactor duplicate Firestore document IDs in Swift? 【发布时间】:2021-08-05 13:59:28 【问题描述】:

我正在使用 Cloud Firestore 做我的第一个 ios 应用程序,并且必须重复对我的数据库进行相同的查询。我想摆脱重复的代码行。这是文档 ID 重复的 func 示例。我还使用其他查询作为.delete().addSnapshotListener().setData()。我应该以某种方式重构所有这些查询,还是因为它们只用过一次就留下它们?

    @objc func updateUI() 

        inputTranslate.text = ""
        inputTranslate.backgroundColor = UIColor.clear

        let user =  Auth.auth().currentUser?.email
        let docRef = db.collection(K.FStore.collectionName).document(user!)
        docRef.getDocument  [self] (document, error) in

        if let document = document, document.exists 

            let document = document
            let label = document.data()?.keys.randomElement()!
            self.someNewWord.text = label
            
                // Fit the label into screen
            self.someNewWord.adjustsFontSizeToFitWidth = true
            
            self.checkButton.isHidden = false
            self.inputTranslate.isHidden = false
            self.deleteBtn.isHidden = false

         else 

            self.checkButton.isHidden = true
            self.inputTranslate.isHidden = true
            self.deleteBtn.isHidden = true
            self.someNewWord.adjustsFontSizeToFitWidth = true
            self.someNewWord.text = "Add your first word to translate"

            updateUI()

            
     

          
@IBAction func checkButton(_ sender: UIButton) 
    
    let user =  Auth.auth().currentUser?.email
    let docRef = db.collection(K.FStore.collectionName).document(user!)
    docRef.getDocument  (document, error) in
        let document = document
        
        let label = self.someNewWord.text!
        let currentTranslate = document!.get(label) as? String

        let translateField = self.inputTranslate.text!.lowercased().trimmingCharacters(in: .whitespaces)

        if translateField == currentTranslate 
            self.inputTranslate.backgroundColor = UIColor.green
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2)  [self] in
                self.inputTranslate.backgroundColor = UIColor.clear
                updateUI()

         else 
            self.inputTranslate.backgroundColor = UIColor.red
            self.inputTranslate.shakingAndRedBg()
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2)  [self] in
                self.inputTranslate.backgroundColor = UIColor.clear
                self.inputTranslate.text = ""
            
        
    


func deletCurrentWord () 

    let user =  Auth.auth().currentUser?.email
    let docRef = db.collection(K.FStore.collectionName).document(user!)
    docRef.getDocument  (document, err) in
        let document = document
        if let err = err 
            print("Error getting documents: \(err)")
            
         else 
              
            let  array = document!.data()
            let counter = array!.count
                
        if counter == 1 
                    
                    // The whole document will deleted together with a last word in list.
            let user =  Auth.auth().currentUser?.email
            self.db.collection(K.FStore.collectionName).document(user!).delete()  err in
                if let err = err 
                    print("Error removing document: \(err)")
                 else 
                    self.updateUI()
                
            
                        
         else 
            // A current word will be deleted

            let user =  Auth.auth().currentUser?.email
            let wordForDelete  = self.someNewWord.text!
                    
            self.db.collection(K.FStore.collectionName).document(user!).updateData([
                wordForDelete: FieldValue.delete()
                    ])  err in
                            
                    if let err = err 
                        print("Error updating document: \(err)")
                     else 
                        self.updateUI()
                        
                    
                
            
    

另一个查询示例

       func loadMessages() 
            let user =  Auth.auth().currentUser?.email
            let docRef = db.collection(K.FStore.collectionName).document(user!)
            docRef.addSnapshotListener  (querySnapshot, error) in
            
            self.messages = []
            
            if let e = error 
                print(e)
             else 
                if let snapshotDocuments = querySnapshot?.data()
                    
                    for item in snapshotDocuments 
                        
                        if let key = item.key as? String, let translate = item.value as? String 
                                                   
                            let newMessage = Message(key: key, value: translate)
                            self.messages.append(newMessage)
                        
                    
                        DispatchQueue.main.async 
                            self.messages.sort(by: $0.value > $1.value)
                            self.secondTableView.reloadData()
                            let indexPath = IndexPath(row: self.messages.count - 1, section: 0)
                            self.secondTableView.scrollToRow(at: indexPath, at: .top, animated: false)
                            
                        
                    
                
            
        

【问题讨论】:

【参考方案1】:
enum Error 
    case invalidUser
    case noDocumentFound


func fetchDocument(onError: @escaping (Error) -> (), completion: @escaping (FIRQueryDocument) -> ())  
    guard let user = Auth.auth().currentUser?.email else 
        onError(.invalidUser)
        return
    
    db.collection(K.FStore.collectionName).document(user).getDocument  (document, error) in
        if let error = error 
            onError(.noDocumentFound)
         else 
            completion(document)
        
    


func updateUI() 
    fetchDocument  [weak self] error in
        self?.hideShowViews(shouldHide: true, newWordText: nil)
     completion:  [weak self] document in
        guard document.exists else 
            self?.hideShowViews(shouldHide: true, newWordText: nil)
            return
        
        self?.hideShowViews(shouldHide: false, newWordText: document.data()?.keys.randomElement())
    



private func hideShowViews(shouldHide: Bool, newWordText: String?) 
    checkButton.isHidden = shouldHide
    inputTranslate.isHidden = shouldHide
    deleteBtn.isHidden = shouldHide
    someNewWord.adjustsFontSizeToFitWidth = true
    someNewWord.text = newWordText ?? "Add your first word to translate"

updateUI 方法可以使用简单的保护语句轻松重构,然后将公共代码取出到单独的函数中。我还使用了[weak self],这样就不会发生内存泄漏或保留周期。

现在,您可以对其余方法采用类似的方法。

    使用guard let 而不是if let 以避免嵌套。 使用[weak self] 进行异步调用以避免内存泄漏。 将通用代码取出到单独的方法中,并使用Bool 标志来隐藏/显示视图。

第 3 步更新: 您可以为 getDocument()delete() 等创建类似于异步 API 的方法,完成后您可以更新 UI 或执行任何操作。您还可以创建一个单独的类,并将fetchDocument() 和其他类似方法移入其中并使用它们。

【讨论】:

第 1 步和第 2 步很有用!我感谢你!您能否详细解释一下 step3 如何将通用代码放入单独的方法中?对数据库的引用是一个 if-else 语句,我不知道如何将它拆分为不同的方法。谢谢 你指的是func deletCurrentWord()吗? 我的意思是使用此行的所有方法,包括 func deletCurrentWord()。这是常见的代码行 let user = Auth.auth().currentUser?.email let docRef = db.collection(K.FStore.collectionName).document(user!) docRef.getDocument [weak self] (document, error)在 guard let document = document 我不能将此引用移动到另一个方法中,因为它是 if-else 语句的一部分。你知道有什么办法吗? 哇,这真的是我想要的!这种重构方式很有趣,对我来说很新鲜。看来这种方式真的很合适,我只需要在这个row guard let document, document.exists else 处修复一个错误“Variable binding in a condition requires an initializer” Do you know how to fix it? 这是因为该文档不是可选的,因此您可以使用guard document.exists else 代替。另外,请投票并将答案标记为已接受。谢谢。

以上是关于如何在 Swift 中重构重复的 Firestore 文档 ID?的主要内容,如果未能解决你的问题,请参考以下文章

从海量视图控制器中重构 Swift UIViewPropertyAnimators

swift 在Swift中重构:设置闭包

代码重构:类重构规则(Swift版)

在 Swift 5/Swiftui 中重构 MapView(没有 Coordinators / UIViewRepresentables)

如何在swift中获取变量的类型[重复]

如何在swift3中获取两个日期之间的日历日[重复]