Stormios
Stormios

Reputation: 127

Data is not displayed in TableView from Firebase

I have a 2 problems with displaying data in a table from Firebase.

  1. Nothing displayed in TableView from Firebase
  2. I I can not add a link(child) to a variable

Print is working. I get access to Firebase, but nothing is added to TableView. Please, look at my code and correct where i'm wrong.

It's my model

class Exercises {

    var titleExercise = ""
    var descriptionExercise = ""

    init (titleExercise: String, descriptionExercise: String) {

        self.titleExercise = titleExercise
        self.descriptionExercise = descriptionExercise
    }
}

It's my ViewController

class ExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {




    //MARK: Properties
    var refWorkout: String = ""
    var workout: TrainingProgram?
    var ref: DatabaseReference!


    @IBOutlet weak var tableView: UITableView!
    var exercises = [Exercises]()


    //MARK: Methods
    override func viewDidLoad() {
        super.viewDidLoad()

        fetchExercises()
        tableView.dataSource = self
        tableView.delegate = self

        refWorkout = workout!.title





    }

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

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

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

        let workouts = exercises[indexPath.item]
        cell.titleLabel.text = workouts.titleExercise
        cell.descriptionLabel.text = workouts.descriptionExercise

        return cell
    }

    func fetchExercises() {
        Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
            print(snapshot.value)
            if let dict = snapshot.value as? [String: AnyObject] {
                let newTitle = dict["title"] as! String
                let newDescription = dict["description"] as! String
                let exerciseTableCell = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)

            }
        }
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }


    }


}

And I have second question. It also addresses this issue. As you can see, I have refWorkout = workout!.title Here comes the title from previous ViewController , and refWorkout is a child for Firebase. If I will write next code

ref = Database.database().reference().child("programs").child(refWorkout).child("exercises")

   ref.observe(.childAdded) { (snapshot) in
       print(snapshot.value)
   }

Everything will work. Print will work. But if I insert this code to func fetchExercises() - > It will look like

func fetchExercises() {
        Database.database().reference().child("programs").child(refWorkout).child("exercises").observe(.childAdded)...

My app crashed. Please help me with two questions. Thank you!

My Firebase structure image

Upvotes: 0

Views: 110

Answers (1)

vadian
vadian

Reputation: 285079

This is a common mistake, you are reloading the table view too soon and you don't assign/append the result to the data source array

The observe API works asynchronously, put the line to reload the table view into the closure

func fetchExercises() {
    Database.database().reference().child("programs").child("OPEN SPACE").child("exercises").observe(.childAdded) { (snapshot) in
        print(snapshot.value)
        if let dict = snapshot.value as? [String: Any] { // most likely all values are value type
            let newTitle = dict["title"] as! String
            let newDescription = dict["description"] as! String
            let exercise = Exercises(titleExercise: newTitle, descriptionExercise: newDescription)
            self.exercises.append(exercise)

        }
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

Side note:

You class contains 3 bad practices:

  1. Semantically objects used in collection types should be named in singular form.
  2. Don't declare properties with default values if there is an initializer.
  3. There is too much redundant information in the variable names

And in most cases a struct and even constants are sufficient. I'd recommend

struct Exercise {
    let title : String
    let description : String
}

In a struct you get the initializer for free.

Upvotes: 1

Related Questions