Search code examples
swiftfirebaseuitableviewgoogle-cloud-firestorequerying

How to make the cell text label a specifc field in a document in a Firestore collection?


So my goal is to make the tableview cells in my dashboard populate with the specific field "event_name" of the documents in my Firestore collection. Here is my code that I came up with by using the Firebase docs.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: Constants.CellDetails.cellName, for: indexPath) as! SwipeTableViewCell
    cell.delegate = self
 
    db.collection("school_users").whereField("event_cost", isEqualTo: "Free")
        .getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {
                    cell.textLabel?.text = "\(document.data())"
                }
            }
    }
    

So basically, what I expected to happen was that the code would magically have the field name populated in the cell, unrealistic I know. I will provide a screenshot of what actually happened .

Code Outcome

So yeah, if anybody knows a way I can do a very specific query for the field and be able to populate it within the cells, that would be great. Thanks.


Solution

  • There are a couple problems going on here.

    The first is that you're doing your Firebase query within cellForRowAt. While that technically works, it is going to fire for each cell on the table and be really inefficient. You should move that API call to somewhere else where you have more control of where it is happening (viewDidLoad is probably the most basic example) and then store the results so that you can access them later from cellForRowAt

    Once, you've performed the query, document.data() returns a [String:Any] so you can pick out a field by doing something like this:

    let eventName = document.data()["event_name"]
    

    Or you can use document.get("event_name")

    You'd want to store either just the names, or better yet, objects representing the events with all the fields in somewhere (aka var events : [Event]) and then you can fill your cell in cellForRowAt by doing something like:

    cell.textLabel?.text = events[indexPath.row].eventName
    

    There's more documentation on getting data from Firebase and converting that data to objects in the Firebase documentation: https://firebase.google.com/docs/firestore/query-data/get-data

    An (untested) sample of what some of this could look like. In real life, you'd want to handle errors, not force unwrap anything, etc.

    struct Event {
        var eventName : String
    }
    
    class FBViewController : UITableViewController {
        var events = [Event]()
        
        override func viewDidLoad() {
            db.collection("school_users").whereField("event_cost", isEqualTo: "Free")
                    .getDocuments() { (querySnapshot, err) in
                        if let err = err {
                            print("Error getting documents: \(err)")
                        } else {
                            self.events = querySnapshot!.documents.map { document in
                                return Event(eventName: (document.get("event_name") as? String) ?? "")
                            }
                            self.tableView.reloadData()
                        }
                }
        }
        
    //Other table view functions....
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: Constants.CellDetails.cellName, for: indexPath) as! SwipeTableViewCell
            cell.textLabel?.text = events[indexPath.row].eventName
        }
    }