Как читать несколько пользовательских объектов из Firestore в Swift - PullRequest
1 голос
/ 19 апреля 2020

Используя пример из документации (для Swift). Вот рекомендуемый способ чтения нескольких документов:

db.collection("cities").whereField("capital", isEqualTo: true)
    .getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
            for document in querySnapshot!.documents {
                print("\(document.documentID) => \(document.data())")
            }
        }
}

А вот рекомендуемый способ чтения пользовательского объекта:

let docRef = db.collection("cities").document("BJ")

docRef.getDocument { (document, error) in
    let result = Result {
        try document.flatMap {
            try $0.data(as: City.self)
        }
    }
    switch result {
    case .success(let city):
        if let city = city {
            print("City: \(city)")
        } else {
            print("Document does not exist")
        }
    case .failure(let error):
        print("Error decoding city: \(error)")
    }
}

Нет примера для чтения нескольких пользовательских объектов. Это была моя попытка, основанная на предыдущих двух примерах:

db.collection("cities").whereField("capital", isEqualTo: true)
    .getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
            if let snapshotDocuments = querySnapshot?.documents {
                for document in snapshotDocuments {        
                    let result = Result {
                        try document.flatMap { // error: Value of type 'QueryDocumentSnapshot' has no member 'flatMap'
                            try $0.data(as: City.self)
                        }
                    }
                    switch result {
                    case .success(let city):
                        if let city = city {
                            print("City: \(city)")
                        } else {
                            print("Document does not exist")
                        }
                    case .failure(let error):
                        print("Error decoding city: \(error)")
                    }
                }
            }
        }
    }
}

На основании ошибки (я поместил ее содержимое в комментарии в приведенном выше коде) Я предполагаю, что классы объектов документа отличаются при запросе для одного объекта или нескольких, хотя их обычное использование одинаково. Как изменить код для загрузки нескольких пользовательских объектов, чтобы он работал?

1 Ответ

2 голосов
/ 20 апреля 2020

В первом примере getDocument возвращает DocumentSnapshot, который является подмножеством DocumentSnapshot? который является необязательным, поэтому его можно использовать с объектом Results .flatmap.

Во втором примере getDocuments не возвращает DocumentSnapshot, он возвращает QueryDocumentSnapshot:

Гарантия QueryDocumentSnapshots что их содержимое всегда ненулевое

Это означает, что оно не является необязательным и не может использоваться с .flatmap, поскольку его нельзя развернуть.

Документация Firebase показывает QueryDocumentSnapshot является подклассом DocumentSnapshot, DocumentSnapshot является необязательным, QueryDocumentSnapshot не является.

В этом случае .flatmap используется для развертывания DocumentSnapshot (необязательный) - происходит то, что объект в Result будет быть необязательным без него. Таким образом .flatmap разворачивает его один раз, а затем if let city = city { снова разворачивает его, и в итоге он становится просто объектом города.

Вот что я хотел бы сделать ... Заменить этот код чем-то вроде

func getCities() {
    let docRef = db.collection("cities")
    docRef.getDocuments { (querySnapshot, error) in
        if let snapshotDocuments = querySnapshot?.documents {
            for document in snapshotDocuments {
                do {
                    if let city = try document.data(as: City.self) {
                        print(city.name)
                    }
                } catch let error as NSError {
                    print("error: \(error.localizedDescription)")
                }
            }
        }
    }
}

EDIT

Я собираюсь позаимствовать это у github, потому что это делает использование результатов (как показано в документации) намного более понятным.

There are thus three cases to handle, which Swift lets us describe
nicely with built-in sum types:

      Result
        /\
   Error  Optional<City>
               /\
            Nil  City //<- we get the actual city with `if let city = city {`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...