首页 > 解决方案 > 如何检索 Cloud Firestore 文档并在 TableView 中显示数据?

问题描述

我有我的数据结构:我的 Firestore 数据库

正如您将看到的,我有一个“Michael 201A”文档和一个“Michael 201B”,其想法是从这些文档中检索字段并将它们显示在 tableView 中。此外,我希望 tableView 根据添加到“请求”集合中的任何新文档自动更新,因此 tableView 数据始终填充最新添加到 firestore 数据库的内容。

从 FireStore 检索数据的函数

@IBOutlet weak var tableView: UITableView!

var db: Firestore!

var requestArray = [Request]()


override func viewDidLoad() {
    super.viewDidLoad()

    db = Firestore.firestore()
    tableView.delegate = self
    tableView.dataSource = self
    loadData()
}

    func loadData() {

    db.collection("Requests").whereField("Status", isEqualTo: true).getDocuments() {(querySnapshot, err) in
        if let err = err {
            print("An error occurred\(err)")
        } else{
            self.requestArray = querySnapshot!.documents.compactMap({Request(dictionary: $0.data())})
            print(self.requestArray)
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
            }


   }
}

我添加了一个打印语句来读取值,但它返回空。

我的 tableView 功能

extension ResidentAdvisorViewController: UITableViewDelegate {

    func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
       print("You tapped me")
   }
}

extension ResidentAdvisorViewController: UITableViewDataSource {

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
       // #warning Incomplete implementation, return the number of rows
    return requestArray.count
   }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

         let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        let request = requestArray[indexPath.row]


        cell.textLabel?.text = "\(request.Name)"
        return cell
    }
}

我的请求结构

protocol DocumentSerializable {
init?(dictionary:[String:Any])

}

struct Request {
var Name: String
var Dorm: String
var Room: Int
var Status: Bool
var UID: String
var TimeStamp: Date

var dictionary:[String:Any] {
return [
    "Name":Name,
    "Dorm":Dorm,
    "Room":Room,
    "Status":Status,
    "UID": UID,
    "TimeStamp": TimeStamp
    ]
}

}

extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
    guard let name = dictionary["Name"] as? String,
    let dorm = dictionary["Dorm"] as? String,
        let room = dictionary["Room"] as? Int,
        let status =  dictionary["Status"] as? Bool,
        let uid = dictionary["UID"] as? String,
        let timestamp = dictionary["Timestamp"] as? Date
    else { return nil}

    self.init(Name: name, Dorm: dorm, Room: room, Status: status, UID: uid, TimeStamp: timestamp)

} }

作为旁注,我已检查以确保我的单元格标识符与“单元格”匹配。此外,当我将单元格文本更改为“Hello World”时,我可以让它显示在我的 tableView 中。非常感谢您的任何帮助,谢谢。

标签: iosswiftfirebaseuitableviewgoogle-cloud-firestore

解决方案


代码没有太多问题,但问题中有两个问题。

1)为什么值为空

2)如何我最初填充我的数据源,然后在添加新文档时更新它。

让我先谈谈2)。

要最初加载数据然后观察未来的变化,我们可以使用 .addSnapshotListener函数,然后在 firebase 闭包中处理特定的变化类型。

func observeAllRequests() {
    let requestsCollection = self.db.collection("Requests")
    let query = requestsCollection.whereField("Status", isEqualTo: true)
    query.addSnapshotListener { querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error fetching snapshots: \(error!)")
            return
        }

        snapshot.documentChanges.forEach { diff in
            if (diff.type == .added) {
                let name = diff.document.get("Name") as? String ?? "No Name"
                print("added: \(name)") //add to your dataSource
            }
            if (diff.type == .modified) {
                let name = diff.document.get("Name") as? String ?? "No Name"
                print("modified: \(name)") //update the request in the dataSource
            }
            if (diff.type == .removed) {
                let name = diff.document.get("Name") as? String ?? "No Name"
                print("removed: \(name)") //remove the request from the dataSource
            }
        }
        //tableView.reloadData()
    }
}

上面的代码将返回与查询匹配的所有文档。迭代快照中的项目,每个项目都是 .add、.modified 或 .removed。第一次调用该函数时,所有差异都将是 .childAdded,它允许您最初填充数据源。

此后的任何文档更改都将只是更改的文档,不同之处在于 .add、.modified 和 .removed。

编辑:

解决问题 1)

数组为空的原因是扩展的结构方式 - 它几乎是全有或全无。现在是这样的

extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
    guard let name = dictionary["name"] as? String
    let dorm = dictionary["Dorm"] as? String,
        let room = dictionary["Room"] as? Int,
        let status =  dictionary["Status"] as? Bool,
        let uid = dictionary["UID"] as? String,
        let timestamp = dictionary["Timestamp"] as? String
    else { return nil}

    self.init(Name: name)
} }

如果没有找到一个字段,那么整个事情就会失败并返回 nil,而 compactMap 忽略 nil 所以你最终会得到一个空数组。您的结构不包括时间戳,因此它失败了。

我会建议一些东西来保护您的代码,但允许缺少字段。nil-coalescing 运算符在这里可以很好地工作

extension Request : DocumentSerializable {
   init?(dictionary: [String : Any]) {
      let name = dictionary["name"] as? String ?? "No Name"
      let room = dictionary["room") as? String ?? "No Room"
      etc

推荐阅读