【问题标题】:Swift Firebase Custom Object with Document ID带有文档 ID 的 Swift Firebase 自定义对象
【发布时间】:2020-08-07 03:29:07
【问题描述】:

有没有办法创建自定义 Swift 对象,我将 Firebase 文档 ID 分配给对象参数?

代码取自这里:https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects

public struct City: Codable {
    let id: String // here I'd like to fill in the Document ID
    let name: String?
    let state: String?
    let country: String?
    let isCapital: Bool?
    let population: Int64?

    enum CodingKeys: String, CodingKey {
        case id = //Document ID
        case name
        case state
        case country
        case isCapital
        case population
    }
}

获取 Firebase 文档并创建对象。代码也取自上面的 Goolge Firebase 链接。

fun getObject(){

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

docRef.getDocument { (document, error) in
    if let error = error {
        print("Error retrieving document: \(error)")
        return
    }
    let result = Result {
      try document?.data(as: City.self)
    }
    switch result {
    case .success(let city):
        if let city = city {
            // A `City` value was successfully initialized from the DocumentSnapshot.
            print("City: \(city)")
        } else {
            // A nil value was successfully initialized from the DocumentSnapshot,
            // or the DocumentSnapshot was nil.
            print("Document does not exist")
        }
    case .failure(let error):
        // A `City` value could not be initialized from the DocumentSnapshot.
        print("Error decoding city: \(error)")
    }
}
}```

【问题讨论】:

    标签: swift firebase swiftui


    【解决方案1】:

    使用我们最近添加到 Firestore 的 Codable 支持,还有一种更简单的方法可以实现这一点:

    添加FirebaseFirestoreSwift pod:

    # Uncomment the next line to define a global platform for your project
    platform :ios, '13.0'
    
    target 'MyAwesomeApp' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for MyAwesomeApp
      pod 'Firebase/Analytics'
      pod 'Firebase/Firestore'
      pod 'FirebaseFirestoreSwift'
    end
    
    import FirebaseFirestoreSwift
    struct City: Codable, Identifiable {
      @DocumentID var id: String? = UUID().uuidString
      let name: String
      let state: String
      let country: String
      let isCapital: Bool
      let population: Int64
    
      // you only need CodingKeys if your attribute names in Firestore differ from the property names on your struct!
    }
    

    通过使用 @DocumentID 属性包装器,您可以告诉 Firestore 的 Codable 支持您希望它将文档的 ID 映射到 id 字段。另请注意 - 由于 City 结构实现了 Identifiable - 您将能够在 SwiftUI ListView 中使用它。

    然后,在您的视图模型中,使用queryDocumentSnapshot.data(as:) 以类型安全的方式获取和映射数据:

    import FirebaseFirestore
    
    class CitiesViewModel: ObservableObject {
      @Published var cities = [City]()
    
      private var db = Firestore.firestore()
      private var listenerRegistration: ListenerRegistration?
    
      deinit {
        unregister()
      }
    
      func unregister() {
        if listenerRegistration != nil {
          listenerRegistration?.remove()
        }
      }
    
      func fetchData() {
        unregister()
        listenerRegistration = db.collection("cities").addSnapshotListener { (querySnapshot, error) in
          guard let documents = querySnapshot?.documents else {
            print("No documents")
            return
          }
    
          self.cities = documents.compactMap { queryDocumentSnapshot -> City? in
            return try? queryDocumentSnapshot.data(as: City.self)
          }
        }
      }
    }
    
    

    【讨论】:

    • 如果有记录就好了。见this question and answer
    • @Simon 这是一个很好的答案,需要考虑一些事项;它将省略每个文档的实际 documentID(密钥),因此 documentID 还必须作为字段存储在每个 Firestore 文档中,我认为这不是您在问题中的预期行为.它如图所示工作,但可能有些多余。另外,请记住,使用此代码,任何更改都将重新加载整个城市列表,而不是处理特定事件——这将增加成本。换句话说,如果有 10,000 个城市,并且如果更新一个城市的人口,它们都会重新加载。
    • 我认为 self.books 应该是 self.cities?
    • 你是对的 @Jay - self.books 应该是 self.cities - 我已经解决了这个问题。我还在您之前的评论中谈到了您关于 id 属性的评论。当我第一次写答案时,我忘了添加 @DocumentID 属性包装器——这是 Firestore 的 Codable 支持的关键特性之一。现在添加了,这段代码应该满足@Simon 的要求。
    • 我试图弄清楚当我的模型(出于架构原因)需要成为一个类时如何使用@DocumentID。我一直把 Id 找回为零。有人遇到过吗?
    【解决方案2】:

    问题中的代码从文档中获取信息并将其映射到城市对象。如果您想将 documentId 分配给该城市对象,则可以这样做

    switch result {
    case .success(let city):
        if var city = city {
            city.docId = document!.documentID
            print("City: \(city)")
        } else {
            print("Document does not exist")
        }
    case .failure(let error):
        print("Error decoding city: \(error)")
    }
    

    注意var city,这样您就可以将 documentID 分配给它

    city.docId = document!.documentID  //please safely unwrap optionals (!)
    

    别忘了这一点

    public struct City: Codable {
       var docId = ""    //needs to be a var
       let name: String
    

    【讨论】:

    • 确实有效。非常感谢您的贡献!
    • 我建议使用 Firestore 的 Codable 支持,它允许您执行从 Firestore 文档到 Swift 结构的类型安全映射。
    猜你喜欢
    • 2022-06-30
    • 2019-05-04
    • 2022-01-18
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 2018-08-11
    相关资源
    最近更新 更多