如何在 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 5/Swiftui 中重构 MapView(没有 Coordinators / UIViewRepresentables)