-3

I have data in my db and can search for an individual record, that's working fine. But when I try to simply populate a tableview with all of the db records its not receiving/displaying any data.

here is my code:

struct drinkStruct {
    let pub: String!
    let rating: String!
    let price: String!
}

override func viewDidLoad() {
    super.viewDidLoad()
    loadDrinks()
    }

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

@IBAction func homeClicked(_ sender: Any) {
    homeClicked()
}


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


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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

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

    let label1 = cell.viewWithTag(1) as! UILabel
    label1.text = posts[indexPath.row].pub

    let label2 = cell.viewWithTag(2) as! UILabel
    label2.text = posts[indexPath.row].rating

    let label3 = cell.viewWithTag(3) as! UILabel
    label3.text = posts[indexPath.row].price


    return cell
}


func loadDrinks(){

    let databaseRef = Database.database().reference().child("Drinks")

    ref = Database.database().reference()
    databaseRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in

        if let valueDictionary = snapshot.value as? [AnyHashable:String]
        {
            let pub = valueDictionary["pub"]
            let rating = valueDictionary["rating"]
            let price = valueDictionary["price"]
            self.posts.insert(drinkStruct(pub: pub, rating: rating, price: price), at: 0)


        }


    })
    self.tableview.reloadData()
}

And here is my db structure: enter image description here

Am I doing something blatantly obviously wrong? Or can anyone see what's causing no data to load?

There are no errors/unused variables etc etc.

Thanks in advance!

Elio Lako
  • 1,333
  • 2
  • 16
  • 26
Eoghan Casey
  • 190
  • 3
  • 17
  • The callback is asynchronous so your are calling tableView.reloadData() before anything is downloaded – Joakim Danielson Mar 01 '19 at 11:24
  • @JoakimDanielson Thanks for the response - so when should I call it? – Eoghan Casey Mar 01 '19 at 11:28
  • You need a data model. – El Tomato Mar 01 '19 at 11:50
  • Use the following fast enumeration to get a dictionary for each array element. for drinks in snapshot.children.allObjects as! [DataSnapshot] { } – El Tomato Mar 01 '19 at 11:52
  • @ElTomato Thank you for the response! I have the Struct for the data I want to get, drinksStruct. Will that not suffice? & where would I put in the snapshot code you commented? – Eoghan Casey Mar 01 '19 at 11:56
  • Inside the closure but like this: `dispatch_async(dispatch_get_main_queue()) { self.tableView.reloadData() }`, see [this answer](https://stackoverflow.com/questions/26277371/swift-uitableview-reloaddata-in-a-closure) – Joakim Danielson Mar 01 '19 at 12:07
  • 1
    @JoakimDanielson While that was a great answer to *that* question, it is unrelated to Firebase closures as they behave differently. UI calls in Firebase closures operate on the main thread so the dispatch call is not needed. See [this answer](https://stackoverflow.com/questions/47490768/how-does-dispatch-main-async-update-the-ui/47490847#47490847) as well as @frankvanpuffelen comment to [this question](https://stackoverflow.com/questions/43106285/firebase-freezes-ui-even-when-using-dispatchqueue). Simply move the `self.tableview.reloadData()` inside the closure. – Jay Mar 02 '19 at 13:58
  • As a side note, if you're loading a LOT of data via .childAdded, you may experience flicker in your UI as the refresh is being called with each child. You may want to consider loading all of the data, populating the dataSource and calling refresh once. That will depend on how much data you're working with. There are also other methods combining .childAdded and .value that will help with larger datasets. And more info [here](https://stackoverflow.com/questions/39369433/do-we-need-to-use-background-thread-for-retrieving-data-using-firebase#39372597) about dispatch queues not being needed for UI. – Jay Mar 02 '19 at 14:03
  • @Jay, thanks for the feedback. – Joakim Danielson Mar 02 '19 at 14:15

2 Answers2

1

I think the following should do the job.

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    override func viewDidLoad() {
        super.viewDidLoad()

        //getting a reference to the node //
        databaseRef = Database.database().reference().child("Drinks")

        //observing the data changes
        databaseRef.observe(DataEventType.value, with: { (snapshot) in
            if snapshot.childrenCount > 0 {
                // clearing the list //
                self.posts.removeAll()

                // iterating through all the values //
                for drinks in snapshot.children.allObjects as! [DataSnapshot] {
                    let drinkObject = drinks.value as! [String: AnyObject]
                    let drinkPub  = drinkObject["pub"]
                    let drinkRating  = drinkObject["rating"]
                    let drinkPrice = drinkObject["price"]

                    //creating a drinkStruct object with the model //
                    let drinkModel = drinkStruct(pub: drinkPub as! String?, rating: drinkRating as! String?, price: drinkPrice as! String?)

                    //appending it to list
                    self.posts.append(drinkModel)
                }
                // reloading data //
                self.tableView.reloadData()
            }
        })
    }

    var posts = [drinkStruct]()
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return posts.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCustomTableViewCell
        let drink: drinkStruct
        drink = posts[indexPath.row]
        cell.label1.text = drink.pub
        cell.label2.text = drink.rating
        cell.label3.text = drink.price
        return cell
    }
}
El Tomato
  • 6,479
  • 6
  • 46
  • 75
  • Thanks for this - few issues with it, I'm getting an error on your func tableView - saying "Use of undeclared type 'YourCustomTableViewCell' " And then "Value of type 'UITableViewCell' has no member 'label1'" + label 2 & 3. Thanks for this I appreciate the help! – Eoghan Casey Mar 01 '19 at 12:47
  • You are not telling how you are handling your table view cell. – El Tomato Mar 01 '19 at 12:49
  • This is completely new to me - I've never worked with a table view before. How do I tell how to handle a tableview cell? – Eoghan Casey Mar 01 '19 at 12:55
  • 1
    This is a solid answer. I would probably change `let drinkPub = drinkObject["pub"]` to `let drinkPub = drinkObject["pub"] as? String ?? "No pub"` to both unwrap the optional and protect the code. @EoghanCasey asking how to work with tableView's goes beyond the scope of the question and you need to get some of the basics down. There are a number of tutorials available on the internet but in particular check out Ray Wenderlich's guides as they are some of the better ones [TableView tutorial](https://www.raywenderlich.com/5995-beginning-table-views) – Jay Mar 01 '19 at 17:45
0

For the newbie that's here in my footsteps, I solved this by doing a lot of things.

You need to create the tableview & cell layout in the storyboard. Then you need a cell class that dictates/assigns what's happening in each cell(imageviews, labels etc) as well as a model class for whatever you're looking up, whatever the object may be.

This is the code I used for my function in which I populate the info in the cells with the data from Firebase:

 func loadDrinks(){
    Database.database().reference().child("Drinks").observe(.childAdded) { (snapshot: DataSnapshot) in
        if let dict = snapshot.value as? [String: Any] {
            let pub = dict["pub"] as! String
            let rating = dict["rating"] as! String
            let price = dict["price"] as! String

            let drink = Drink(pub: pub.capitalized, rating: rating.capitalized, price: price.capitalized)
            self.drinks.append(drink)
            print(self.drinks)
            self.tableview.reloadData()

        }
    }
}

This was a Newbie 101 question - my bad.

Eoghan Casey
  • 190
  • 3
  • 17