oscarfrederiksen
oscarfrederiksen

Reputation: 115

Populating tableview with firebase data (array not updating within .childAdded)

I know the title of the post is confusing however I'll hopefully clear it up here.

My end goal:

Basically I want to populate a tableview using data obtained from the firebase realtime database.

My issue

Through a lot of troubleshooting I believe my issue is that when I append something to the modelObjectsArray within the .childAdded closure it doesn't seem to actually update the array.

My code

View Controller

import Foundation
import UIKit
import Firebase

class CategoryDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var tableView: UITableView!
    static let cellReuseIdentifier = "ratingCell"
    
    var modelObjectArray: [Rating] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        retrieveData()
        print(modelObjectArray) // prints [] for some reason...
//        print(modelObjectArray.count) // prints 0     
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        tableView.delegate = self
        tableView.dataSource = self
        
//        modelObjectArray.append(Rating(score: 3, comment: "hahahaha, I really do enjoy beans...", timestamp: "Just now", likes: 10)) -> temporary to see if the tableview works (which it does)
        tableView.reloadData()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return modelObjectArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell: CategoryDetailTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "ratingCell") as! CategoryDetailTableViewCell
        
        let modelObject = self.modelObjectArray[indexPath.row]
        cell.commentLabel.text = modelObject.comment
        cell.timestampLabel.text = String(modelObject.timestamp) + " hours ago"
        cell.ratingLabel.text = String(modelObject.score) + "/10"
        
        return cell
    }
    
    func retrieveData(){
        let databaseRef: DatabaseReference = Database.database().reference()
        databaseRef.child("uid").child("categories").child("looks").child("ratings").observe(.childAdded, with: { snapshot in
            if let value = snapshot.value as? [String:Any] {
                let rating = Rating(score: value["score"] as? Int ?? 0, comment: value["comment"] as? String ?? "", timestamp: value["creationDate"] as? String ?? "Just now", likes: value["likes"] as? Int ?? 0)
                self.modelObjectArray.append(rating)
                print(self.modelObjectArray.count) // prints 2

            }
        })
        self.tableView.reloadData()
    }

}

Rating Model

class Rating {
    var score: Int
    var comment: String
    var timestamp: String
    var likes: Int
    
    init(score: Int, comment: String, timestamp: String, likes: Int) {
        self.score = score
        self.comment = comment
        self.timestamp = timestamp
        self.likes = likes
    }
}

My Firebase

https://i.sstatic.net/uBhZt.jpg

Overall

I feel like I've left out way too much information so just tell me if you need anymore. (Also hopefully I didn't delete any important code when cleaning it up for the post - just tell if I might've)

Anyway, thanks in advance for anyone who answers!

Upvotes: 1

Views: 96

Answers (1)

Jawad Ali
Jawad Ali

Reputation: 14407

Reload data inside async block .. otherwise reload data execute before your data fetched from firebase

 func retrieveData(){
        let databaseRef: DatabaseReference = Database.database().reference()
        databaseRef.child("uid").child("categories").child("looks").child("ratings").observe(.childAdded, with: { snapshot in
            if let value = snapshot.value as? [String:Any] {
                let rating = Rating(score: value["score"] as? Int ?? 0, comment: value["comment"] as? String ?? "", timestamp: value["creationDate"] as? String ?? "Just now", likes: value["likes"] as? Int ?? 0)
                self.modelObjectArray.append(rating)
                print(self.modelObjectArray.count) // prints 2

                 self.tableView.reloadData()
                
            }
        })
       
    }

Second Approach is to use completion handler

func retrieveData(completion:@escaping (Bool)->Void) {
    let databaseRef: DatabaseReference = Database.database().reference()
    databaseRef.child("uid").child("categories").child("looks").child("ratings").observe(.childAdded, with: { snapshot in
        if let value = snapshot.value as? [String:Any] {
            let rating = Rating(score: value["score"] as? Int ?? 0, comment: value["comment"] as? String ?? "", timestamp: value["creationDate"] as? String ?? "Just now", likes: value["likes"] as? Int ?? 0)
            self.modelObjectArray.append(rating)
            print(self.modelObjectArray.count) // prints 2

            completion(true)
            
        }else {
              completion(false)
        }
    })
   
} 

Use it like this

override func viewDidLoad() {
        super.viewDidLoad()
        retrieveData { [weak self] (isSuccess) in
            self?.tableViewe.reloadData()
        }
        
        // Do any additional setup after loading the view.
    }

Upvotes: 2

Related Questions