mikro098
mikro098

Reputation: 2343

Iterating through all nodes in node in Firebase

I've got sample structure that looks like this: enter image description here

I want to add every item to the array of items I've created. So as you can see the downloadListData function can only download information from Apples, because I don't know how to get to Apples, Bread and Eggs in the same time without writing a lot of code. I was trying loops and arrays and it didn't work. Moreovery I was analyzing examples on the Internet and I didn't get the answer which worked in my app.

ListItem.swift:

import Foundation
import Firebase

struct ListItem{
    var name : String!
    var addedBy : String!
    var completed : Bool!

    init(name: String, addedBy: String, completed: Bool){
        self.name = name
        self.addedBy = addedBy
        self.completed = completed
    }
}

part of my ViewController.swift:

import UIKit
import Firebase

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var rootRef : FIRDatabaseReference!
    var listDataRef : FIRDatabaseReference!

    var refHandle: UInt!

    var listItemsDownload = [ListItem]()

    //test vars
    var user : String!

    @IBOutlet weak var plusButton: UIButton!
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.delegate = self
        tableView.dataSource = self
        user = "[email protected]"


        rootRef = FIRDatabase.database().reference()
        listDataRef = rootRef.child("listData")            

        downloadListData()
    }

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

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CellData{

        print("cellForRowAt")
        let items = listItemsDownload[indexPath.row]
        // testing the tableView            
        cell.nameLabel?.text = items.name

        }
        return cell
    } else{
        print("else")
        return CellData()
    }
}
func downloadListData(){
    print(" ### DOWNLOAD ###")
    self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
        var downloadedName : String!
        var downloadedUser : String!
        var downloadedComplete : Bool!

        if let dict = snapshot.value as? Dictionary<String, Any>{
            if let apples = dict["Apples"] as? Dictionary<String, Any>{

                if let name = apples["name"] as? String{
                    downloadedName = name
                }

                if let user = apples["addedBy"] as? String{
                    downloadedUser = user
                }

                if let completed = apples["completed"] as? Bool{
                    downloadedComplete = completed
                }

                let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)

                self.listItemsDownload.append(item)

                self.tableView.reloadData()

            }
        }
    })
}

So probably I have to change only this line to get to different values and not only Apples (if let apples = dict["Apples"] as? Dictionary<String, Any>{

Upvotes: 1

Views: 411

Answers (1)

ronatory
ronatory

Reputation: 7324

Just use a separate method which you can call with your different keys like this:

  func downloadListData(){
    print(" ### DOWNLOAD ###")
    self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
        addAllItemsFromSnapshotWithKey(snapshot, key: "Apples")
        addAllItemsFromSnapshotWithKey(snapshot, key: "Bread")
        addAllItemsFromSnapshotWithKey(snapshot, key: "Eggs")
        // You need to reload your table view on the main thread, since it's an asynchronous call to firebase
        DispatchQueue.main.async {
          self.tableView.reloadData()  
        }
    })
  }

  func addAllItemsFromSnapshotWithKey(_ snapshot: FIRDataSnapshot, key: String) {
    var downloadedName : String!
    var downloadedUser : String!
    var downloadedComplete : Bool!

    if let dict = snapshot.value as? Dictionary<String, Any>{
      if let values = dict[key] as? Dictionary<String, Any> {

        if let name = values["name"] as? String{
          downloadedName = name
        }

        if let user = values["addedBy"] as? String{
          downloadedUser = user
        }

        if let completed = values["completed"] as? Bool{
          downloadedComplete = completed
        }

        let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)

        self.listItemsDownload.append(item)

      }
    }
  }

Update for a more scalable solution. Just loop through all keys

 func downloadListData(){
    print(" ### DOWNLOAD ###")
    self.listDataRef.observe(FIRDataEventType.value, with: { snapshot in
      var downloadedName : String!
      var downloadedUser : String!
      var downloadedComplete : Bool!

      if let dict = snapshot.value as? Dictionary<String, Any>{

        for key in dict.keys {

          if let values = dict[key] as? Dictionary<String, Any> {

            if let name = values["name"] as? String{
              downloadedName = name
            }

            if let user = values["addedBy"] as? String{
              downloadedUser = user
            }

            if let completed = values["completed"] as? Bool{
              downloadedComplete = completed
            }

            let item = ListItem(name: downloadedName, addedBy: downloadedUser, completed: downloadedComplete)

            self.listItemsDownload.append(item)
          }
        }
        DispatchQueue.main.async {
          self.tableView.reloadData()
        }
      }
    })
  }

Upvotes: 2

Related Questions