如何检索Cloud Firestore文档并在TableView中显示数据?

时间:2020-04-15 18:59:33

标签: ios swift firebase uitableview google-cloud-firestore

我有我的数据结构:My Firestore Database

您将看到我有一个“ 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()
            }
            }


   }
}

我添加了一条print语句以读取该值,但它返回空值。

我的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)

} }

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

1 个答案:

答案 0 :(得分:1)

代码没有很多错误,但是问题中有两个问题。

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()
    }
}

上面的代码将返回所有与查询匹配的文档。遍历快照中的项目,每个项目都可以添加,修改或删除。第一次调用该函数时,所有差异都将是.childAdded,它使您可以初始填充数据源。

此后任何文档更改都将只是更改后的文档,不同之处在于添加,修改和删除。

编辑:

解决问题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