Automatically Inserting Into a Table View

Hi all,

So I’m using Firebase and I wanted to know if I could insert values into the table view cells automatically. So, the table view grabs some data from Firebase and then displays it in the cells. It’s also listening for new inserts, so it also displays new data. How would I do this?

Thanks!

TableViews don’t update automatically based on a change in the data which is displayed in them. What you have to do is come up with a mechanism to reload the data in the tableView based on monitoring the Firebase data somehow.

Are you looking at this from the point of view of, for example, someone running a business and orders are placed externally which populate the Firebase database. You are monitoring that database and you want the App to update the tableView in response to an order being placed?

1 Like

@Chris_Parker Yep. That’s exactly what I want to do. I’m not only struggling on that, but also struggling on actually getting the data into the tableView. Would I put the data in an array, and if so, how would I do it?

Thanks so much for the reply!

Aha, now I see where you are at. Yes you do have to have an array that mirrors the data structure that you are dealing with.

If you are unfamiliar with Firebase then you probably need to have a look at a tutorial on retrieving and posting records to a Firebase Database. What the Custom Login Demo tutorial covers is just the user logging in. Writing, retrieving and deleting records from the database is another topic altogether.

I have a suggestion. Have a look at this 3 part tutorial from Chris which gets started with Firebase. It covers authentication (which is what you are doing now) and then in the following tutorials, retrieving data from a Firestore database.

1 Like

Hey @Chris_Parker,

Thanks for that reply! I’ve watched that course a little bit, as well as Chris’ videos on how to retrieve and update data in Firestore. Does Chris specifically go over putting Firestore data in arrays in that course?

Thanks!

To be honest have not watched them in detail so can’t comment. I guess I better have a look so I know for sure. :grimacing:

1 Like

Hey @Chris_Parker,

I actually think I got it bc of your help. The part I’m struggling on is where I insert the Firebase key value pairs into an array. How would I do this?

Thank you!

@YourAvgCoder

I watched that video series and I have a rough idea of how that would work.

You would need to create a struct that represents the data side of your key value pairs. Lets say you had key value pairs of "name": "Fred Nurk", "age": 35, "location": "New York"

then your struct would be

struct Customer {
    let name: String
    let age: Int
    let location: String
}

Your array that would be used on your tableView would then be something like:

var customers: [Customer] = [] // Start with an empty array

You would have to make a dB call to get all the records into a snapshot and then populate that customers array with each record from the database.

You would loop through the snapshot.documents as per the example that Chris used and do something like this:

customers.removeAll() 
for document in snapshot.documents {
    let newEntry = Customer(name: document.name, age: document.age, location: document.location)
    customers.append(newEntry)
}

then in your numberOfRowsInSection you would have:
return customers.count

In cellForRowAt you could have a custom cell where you had

let customer = indexPath.row
cell.nameLabel.text = customer.name
cell.ageLabel.text = String(customer.age)
cell.locationLabel.text = customer.location

Does that make sense?

1 Like

@Chris_Parker,

Thank you so much! I have been scouring Google for hours now and am so relieved that you responded. I will definitely try this out!

Once again, thank you so much! I will keep you updated if this works!

Thanks!

Hi @Chris_Parker ,

So I tried that, but I am getting a few errors.

First, Xcode is not recognizing my document.id, document.name, or document.feeling field. The error says:

"Value of type ‘QueryDocumentSnapshot’ has no member ‘date’", and it says this for all the fields. However, these are fields in all my documents.

Secondly, am I supposed to have to put a self. before the customers.removeAll()

Do you have any idea as how to fix these?

Thank you!

Hmmmmm, I’m going to have to take a closer look at this.

I’m assuming that you were able to log in and get to the Welcome screen. If that is the case then were you able to create a record in the database through code?

1 Like

Hi @Chris_Parker,

I didn’t use Chris’ authentication tutorial for my authentication because I already had one set up. I set up a welcome screen where you could create an account or log in. Creating an account would involve creating a new id and password that would then be stored in a collection in my database. However, for logging in, I used the .whereField("id" isEqualTo: user input and .whereField("pass" isEqualTo: user input). So this is all working.

I think the problem is that Xcode is not recognizing the document.xyz field statement. I may be wrong, but that’s where the error is occurring. I don’t really know what to do at this point, because I don’t understand why Swift is not recognizing it.

Thanks so much for your help!

Hi @Chris_Parker and anyone who wants to jump in and help,

Update:

So I’ve fixed all the errors, but when I try to pass the array into the text field, nothing shows up. I’ve set the numberOfRowsInSection to return feedbackarray.count. In my viewDidLoad, I run the function that adds everything to my array. However, the text field remains empty with no cells. Below is my code:

import UIKit
import Firebase

class SummaryEmployeesFeelingViewController: UIViewController, UITableViewDataSource{

struct Feedback {
    let id: Int
    let feeling: String
    let date: Timestamp
}


var feedbackarray = [Feedback]()

@IBOutlet weak var tableView: UITableView!

let summarydb = Firestore.firestore()

override func viewDidLoad() {
    super.viewDidLoad()
    addDatatoArray()
    tableView.dataSource = self
    tableView.tableFooterView = UIView()

}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return feedbackarray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let feedbackcell = tableView.dequeueReusableCell(withIdentifier: "CellReuseIdentifier")!
    
    let text = feedbackarray[indexPath.row] as? String
  
    feedbackcell.textLabel?.text = text
    
    return feedbackcell
}

func addDatatoArray(){
    summarydb.collection("employeefeedback").getDocuments { (snapshot, error) in
        if error == nil && snapshot != nil{
            for document in snapshot!.documents{
                var idValue = document.get("id")
                var feelingValue = document.get("feeling")
                var dateValue = document.get("date")
                let newEntry = Feedback(id: idValue as! Int, feeling: feelingValue as! String, date: dateValue as! Timestamp)
                self.feedbackarray.append(newEntry)
                
            }
    }
}

}

}
`

Any help would be greatly appreciated! Thank you!

Your code is pretty close to what I did with one exception…

I did some testing late last night and ended up in bed after midnight (I do get a bit obsessive when it comes to figuring out these sorts of issues and hate being beaten by any challenge… anyway I succeeded).

I created a struct to mirror the data I was messing around with because I wanted that struct to be the data model for the tableView. ie:

struct User {
    let name: String
    let age: Int
    let address: String
    let city: String
    let postcode: Int
}

In the TableViewController I defined a property which is an array of that type. ie:

var users: [User] = []

In my table methods I had the following

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        users.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as? UserCell else {
            fatalError("Unable to dequeue UserCell")
        }
        let user = users[indexPath.row]
        
        cell.nameLabel.text = user.name
        cell.ageLabel.text = String(user.age)
        cell.addressLabel.text = user.address
        cell.cityLabel.text = user.city
        cell.postcodeLabel.text = String(user.postcode)
        
        return cell
    }

I created a function to read the data and populated the array sort of along the lines you did but this was the code that worked after much hair pulling and table banging. The key was understanding that the data coming back is an array of dictionary items.

func getDatabaseRecords() {
        
        let db = Firestore.firestore()
        
        users = []  //  Empty the array
        db.collection("users").getDocuments { (snapshot, error) in
            if let error = error {
                print(error)
                return
            } else {
                for document in snapshot!.documents {
                    let data = document.data()
                    let newEntry = User(
                        name: data["name"] as! String,
                        age: data["age"] as! Int,
                        address: data["address"] as! String,
                        city: data["city"] as! String,
                        postcode: data["postcode"] as! Int)
                    self.users.append(newEntry)
                }
            }
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }

Hope that gives you a pointer to what you need to do to get your data to work.

Cheers
Chris

1 Like

Hey Chris,

That worked, thanks! I really appreciate it!

That’s awesome.Great to hear. :+1:

1 Like