首页 > 解决方案 > 无法让 Firestore 将快照文档转换为“照片”对象

问题描述

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

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

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

        }
    }
}

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

    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 特定的。

标签: iosswiftfirebasegoogle-cloud-firestore

解决方案


问题中的代码存在许多问题;三种不同的 API(Firebase RealTime Database、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 - 我将属性默认为空字符串而不是 nils,以减少处理 nils 的需要。由于我们没有使用 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)
        }
    })
}

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


推荐阅读