# #ios #swift #firebase #duplicates
Вопрос:
Я создаю свое самое первое приложение для IOS с помощью Cloud Firestore, и мне приходится неоднократно делать одни и те же запросы к своей базе данных. Я хотел бы избавиться от повторяющихся строк кода. Это примеры функций, в которых дублируются идентификаторы документов. Также я использую другие запросы, такие как .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
Метод можно легко реорганизовать, используя простой оператор guard, а затем вывести общий код в отдельную функцию. Я также использовал [weak self]
, чтобы не происходило утечки памяти или циклов сохранения.
Теперь вы можете следовать аналогичному подходу для остальных методов.
- Используйте
guard let
вместоif let
того, чтобы избегать вложенности. - Используйте
[weak self]
для асинхронных вызовов, чтобы избежать утечки памяти. - Выделите общий код в отдельный метод и используйте
Bool
флаг для скрытия/отображения представлений.
Обновление для шага 3: Вы можете создавать методы, аналогичные асинхронным API для getDocument()
или delete()
и т.д., И по завершении вы можете обновить пользовательский интерфейс или выполнить любое действие. Вы также можете создать отдельный класс, переместить fetchDocument()
туда и другие подобные методы и использовать их.
Комментарии:
1. Шаги 1 и 2 были полезны! Я благодарен вам! Не могли бы вы подробнее рассказать о шаге 3, как поместить общий код в отдельный метод? Ссылка на базу данных-это оператор if-else, и я понятия не имею, как разделить его на разные методы. Спасибо
2. Вы имеете в
func deletCurrentWord()
виду ?3. Я имею в виду все методы, в которых это используется, включая функцию deletCurrentWord(). Это обычные строки кода, в которых пользователь = Auth.auth().Текущий пользователь? .электронная почта пусть docRef = db.коллекция(K. FStore.Имя коллекции).документ(пользователь!) docRef.getDocument { [слабое я] (документ, ошибка) в охраннике пусть документ = документ Я не могу переместить эту ссылку в другой метод, потому что это часть if-else заявление. Ты знаешь какой-нибудь способ сделать это?
4. ух ты, это действительно то, что я искал! Этот способ рефакторинга очень интересен, он для меня в новинку. Похоже, что этот способ действительно подходит, мне нужно исправить только одну ошибку «Привязка переменных в состоянии требует инициализатора» в этой строке.Пусть документ, document. exists { Вы знаете, как это исправить?
5. Это потому, что документ не является необязательным, поэтому вы можете использовать
guard document.exists else {
его вместо этого. Кроме того, пожалуйста, повысьте голос и отметьте ответ как принятый. Спасибо.