【问题标题】:Can't get Firestore to convert snapshot documents into 'Photo' objects无法让 Firestore 将快照文档转换为“照片”对象
【发布时间】:2019-09-29 03:41:21
【问题描述】:

我在将使用 Firebase 实时数据库的 getPhotos 方法迁移到使用 Firestore 的 getPhotos1 时遇到问题,但由于某种原因无法将 getPhotos1 中的文档快照获取到 Photos1 对象。

这是我使用 Firebase 实时数据库的 getPhotos 方法:

static func getPhotos (completion: @escaping ([Photo]) -> Void) {

    //Getting a reference to the database
    let dbRef = Database.database().reference()

    //Make the db call
    dbRef.child("photos").observeSingleEvent(of: .value) { (snapshot) in

        //Declare an array to hold the photos
        var retrievedPhotos = [Photo]()

        //Get the list of snapshots
        let snapshots = snapshot.children.allObjects as? [DataSnapshot]

        if let snapshots = snapshots {

            //Loop through each snapshot and parse out the photos
            for snap in snapshots {

                //Try to create a photo from a snapshot
                let p = Photo(snapshot: snap)

                //If successful, then add it to our array
                if p != nil {
                    retrievedPhotos.insert(p!, at: 0)

                }
            }
        }
        //After parsing the snapshots, call the completion closure
        completion(retrievedPhotos)
    }
}

这是它的对象

import Foundation
import RealmSwift
import FirebaseDatabase
import Firebase

class Photo: Object {

    @objc dynamic var photoId:String?
    @objc dynamic var byId:String?
    @objc dynamic var byUsername:String?
    @objc dynamic var date:String?
    @objc dynamic var url:String?
    @objc dynamic var postId:String?

    override static func primaryKey() -> String? {
        return "photoId"
    }

    override static func indexedProperties() -> [String] {
        return ["byUsername"]
    }

    convenience init?(snapshot:DataSnapshot){
        self.init ()

        //Photo data
        let photoData = snapshot.value as? [String:String]

        if let photoData = photoData {

            let photoId = snapshot.key
            let byId = photoData["byId"]
            let byUsername = photoData["byUsername"]
            let date = photoData["date"]
            let url = photoData["url"]
            let postId = photoData["postId"]

            guard byId != nil && byUsername != nil && date != nil && url != nil && postId != nil else {
                return nil
            }
            self.photoId = photoId
            self.byId = byId
            self.byUsername = byUsername
            self.date = date
            self.url = url
            self.postId = postId

        }
    }
}

这是我使用 Firestore 的 getPhotos1 方法,不起作用:

    static func getPhotos1 (completion: @escaping ([Photo1]) -> Void) {

    //Get a Firebase reference
    let db = Firestore.firestore()
    db.collection("posts").getDocuments() {snapshot, error  in

        //Declare an array to hold the photos
        var retrievedPhotos = [Photo1]()

        if error == nil && snapshot != nil {
            for document in snapshot!.documents {
                let p = Photo1(snapshot: document)
                print(p!)
                if p != nil {
                retrievedPhotos.insert(p!, at: 0)
                //print("retrievedPhotos: \(retrievedPhotos)")
                }
            }
        }
        //After parsing the snapshots, call the completion closure
        completion(retrievedPhotos)
    }
}

这就是它的对象

class Photo1: Object {

@objc dynamic var photoId:String?
@objc dynamic var byId:String?
@objc dynamic var byUsername:String?
@objc dynamic var date:String?
@objc dynamic var url:String?
@objc dynamic var postId:String?

override static func primaryKey() -> String? {
    return "photoId"
}

override static func indexedProperties() -> [String] {
    return ["byUsername"]
}

convenience init?(snapshot:DocumentSnapshot){
    self.init ()

    //Photo data
    let photoData = snapshot.data() as? [String:String]

    if let photoData = photoData {

        let photoId = snapshot.documentID
        let byId = photoData["byId"]
        let byUsername = photoData["byUsername"]
        let date = photoData["date"]
        let url = photoData["url"]
        let postId = photoData["postId"]

        guard byId != nil && byUsername != nil && date != nil && url != nil && postId != nil else {
            return nil
        }
        self.photoId = photoId
        self.byId = byId
        self.byUsername = byUsername
        self.date = date
        self.url = url
        self.postId = postId

    }
}
}

10 月 1 日编辑: 更新了 getPhoto1 方法。

static func getPhotos1 (completion: @escaping ([Photo1]) -> Void) {

    //Get a Firebase reference
    let db = Firestore.firestore()
    db.collection("posts").getDocuments() { (snapshot, error)  in

        //Declare an array to hold the photos
        var retrievedPhotos = [Photo1]()
        print("This is snapshot: \(String(describing: snapshot))")

        if error == nil && snapshot != nil {

            for document in snapshot!.documents {

                let documentData = document.data()

                print("This is DocData: \(documentData)")

                let p = Photo1(snapshot: documentData)
                print("This is p: \(p)")
                if p != nil {
                retrievedPhotos.insert(p!, at: 0)
                print("retrievedPhotos: \(retrievedPhotos)")
                }}}
        //After parsing the snapshots, call the completion closure
        completion(retrievedPhotos)
    }}

但是我收到了这个错误:

Cannot convert value of type '[String : Any]' to expected argument type 'DocumentSnapshot'

提前感谢您的建议。

我试图搜索以前的帖子,虽然有一些,但我无法弄清楚,因为它们不是 Swift 特定的。

【问题讨论】:

  • 这个问题似乎不完整。您的 Firestore 代码中没有任何内容与您的 Firebase 实时数据库代码匹配。您正在读取两个不同的节点名称(照片和帖子),您的 Photo 对象使用 RTDB 代码(例如,没有使用 Firestore 代码的对象),最重要的是,您的 Firestore 代码除了阅读文档之外并没有真正做任何事情在您的帖子集合中。你能解决这个问题并澄清你在问什么吗?
  • 在'getPhotos1'方法中删除一些评论//后,我正在尝试做同样的事情,即在p中转换快照文档,这与在'中将snap转换为p相同获取照片'。在照片的对象中,我知道当我开始使用“getPhotos1”方法时,我必须更改一些东西才能使其工作,例如“DataSnapshot”到“Doc​​umentSnapshot”等。我特别需要帮助来获取“getPhotos1”方法像在“getPhotos”中一样,在retrievePhotos 中返回完整的照片数组。现在它返回 nil 或者甚至不会根据我的尝试构建
  • 我想你错过了我的意思。您的代码 let p = Photo(snapshot: document) 将 Firestore DocumentSnapshot 传递给您的 Photo 对象,该对象需要 Firebase 实时数据库 DataSnapshot 对象,并且两者不兼容。因此,不会创建 Photo 对象,也无法将其添加到数组中。您需要更新您的 Photo 对象以使用 Firestore DocumentSnapshot 对象进行初始化。
  • 我为 getPhoto1 更新了 Photo 对象(现在是 Photo1),以确保它使用正确的 Firestore 协议,但 p(在 getPhoto1 方法中)仍然为对象返回 nil
  • 接下来是添加错误检查。如果有任何错误,您的代码会继续运行而不通知您。

标签: ios swift firebase google-cloud-firestore


【解决方案1】:

问题中的代码存在许多问题;我认为这三种不同的 API(Firebase 实时数据库、Firestore 和 Realm)可能会造成一些混乱。

让我们用 Firestore 的直接答案切入正题。此代码将读取 Firestore 集合中的文档,根据该数据创建 PhotoClass 对象并将它们存储在数组中。假设结构是这样的

photos //a collection
   photo_0 //a document
      byUserName: "Henry"
      byId: "uid_0"
   photo_1
      byUserName: "George"
      byId: "uid_1"

这是一个 PhotoClass - 我将属性默认为空字符串而不是 nil,以减少处理 nil 的需要。由于我们没有使用 Realm,因此我还删除了 Object 定义并将 vars 更改为 Swift vars 而不是 Realm 所需的 ObjC vars(@objc 动态)

class PhotoClass {
    var photoId = ""
    var byId = ""
    var byUsername = ""
    //var date:String?
    //var url:String?
    //var postId:String?

    init(withDocumentSnapshot: DocumentSnapshot) {
        self.photoId = withDocumentSnapshot.documentID
        self.byUsername = withDocumentSnapshot.get("userName") as? String ?? "No User Name"
        self.byId = withDocumentSnapshot.get("byId") as? String ?? "No uid"
    }
}

请注意,如果读取的属性为零,我会将值默认为已知字符串。这样,如果属性丢失或为零,应用程序就不会崩溃。如果你想使用 nil 值,那很酷,但你必须通过在整个过程中安全地使用它们来保护你的代码。

然后是一个数组来存储照片的

var photosArray = [PhotoClass]()

然后是读取照片节点中所有文档的代码,创建 PhotoClass 对象并填充数组

func readAllPhotos() {
    let photosCollection = self.db.collection("photos")
    photosCollection.getDocuments(completion: { snapshot, err in
        if let err = err {
            print(err.localizedDescription)
            return
        }

        for doc in snapshot!.documents {
            let photo = PhotoClass(withDocumentSnapshot: doc)
            self.photosArray.append(photo)
        }
    })
}

再次注意,有代码可以检查错误并在未找到节点或发生其他错误时保护应用程序。

【讨论】:

  • 谢谢@Jay!!。这仅适用于 Firebase,目前已经足够了。稍后我会弄清楚 Realm 部分;也许:-)
  • @akash23a 太棒了!虽然我们在项目中广泛使用 Realm,但您应该评估是否需要三个甚至两个不同的数据库。您也许可以只使用 Firestore 而根本不合并 Realm。如果我的回答有帮助,请务必接受它,以便它可以帮助其他人。 How To Accept
  • 回答已接受!现在正式关闭 FBRT,可能会也可能不会合并回 Realm,所以现在降至 1 db
猜你喜欢
  • 2016-10-28
  • 2021-04-02
  • 2022-01-25
  • 2021-09-27
  • 2011-07-26
  • 1970-01-01
  • 2018-03-19
  • 2019-09-03
  • 1970-01-01
相关资源
最近更新 更多