Search code examples
arraysswiftfirebasetableviewnstableheadercell

Swift 5 - Firebase- create array from data in firebase


I am trying to create an array from my data in firebase so that I can load it as a header in my tableview. I understand how to put the data into the section headers when I have the information hard coded in an array in the code but I am unsure how to do so when the data is in Firebase and I am wanting to put that into an array and then put it into the headers. Hopefully this makes sense.

my Firebase data is

usclmainmenu
    events
        USCL Open House
           date: 
              "September 31 2020"
           info: 
              "Opening to new students"
           place: 
              "USC Lancaster"
           time: 
              "8 pm"
           title: 
               "USCL Open House"

My function to get my data from firebase is:

private func getEventsFromFirebaseDB()
       {
           datref = Database.database().reference().child("events")
            datref.observe(DataEventType.value, with: { [weak self] (snapshot) in
           
                   guard snapshot.childrenCount > 0 else { return }
           
                   var events: [EventsInfo] = []
                   for event in snapshot.children.allObjects as! [DataSnapshot]
                   {
                       let object = event.value as? [String: AnyObject]
           
                       let title = object?["title"]
                       let place = object?["place"]
                       let info = object?["info"]
                       let date = object?["date"]
                       let time = object?["time"]
           
                    let event = EventsInfo(title: title as! String, place: place as! String, info: info as! String, time: time as! String, date: date  as! String)
                       events.append(event)
                        }

                self?.eventsFetched(events)
                self?.eventTableView.reloadData()
                    })

          
        }


My data model is:

class EventsInfo {
    
    var title: String
    var place: String
    var info: String
    var time: String
    var date: String
    
    
    init(title: String, place: String, info: String, time: String, date: String) {
        
        self.title = title
        self.place = place
        self.info = info
        self.time = time
        self.date = date

    }
}

This is my extension for my datasource and delegate methods

extension EventsTableViewController: UITableViewDataSource, UITableViewDelegate {
    
    func numberOfSections(in tableView: UITableView) -> Int
    {
        sectionNames.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
      {
          sectionNames[section]
        
        
       
      }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
        {
        let char = sectionNames[section]
           return events[char]!.count
        }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
        {
    
        let cell = eventTableView.dequeueReusableCell(withIdentifier: "eventsCell") as! EventsTableViewCell
        let char = sectionNames[indexPath.section]
        let event = events[char]![indexPath.row]
        cell.eventTitleLabel.text = event.title
        return cell
            
        }


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "showEvent", sender: self)
        }


Solution

  • The string to be used as the section headers wasn't mentioned in the question but based on a prior question I believe the sections are to be event dates. Then the events that happen on that date are to be listed within that date section.

    The code in this answer is untested and it's only one of many options. I also changed the dynamic a bit and put more of the responsibility to handle the snapshots within the EventInfo class logic instead of the loop.

    First, we start with an object to hold the event details. Noting it's initialized with a Firebase Datasnapshot. In case something goes wrong, it leverages default values for the optionals. Also note I added an eventID which keeps track of the Firebase key so the object can be changed or deleted later via that id.

    class EventInfo {
        var eventId: String
        var title: String
        var place: String
        var date: String
        
        init(eventSnap: DataSnapshot) {
            self.eventId = eventSnap.key
            self.title = eventSnap.childSnapshot(forPath: "title").value as? String ?? "No Title"
            self.place = eventSnap.childSnapshot(forPath: "title").value as? String ?? "No Place"
            self.date = eventSnap.childSnapshot(forPath: "date").value as? String ?? "Unknown Date"
        }
    }
    

    then a class var array to hold all of the events. This will function as the datasource for the section headers and tableView data

    class EventSectionHeader {
        var eventDate = ""
        var eventArray = [EventInfo]()
    }
    

    then a class var to hold all of the EventSectionHeaders

    var eventSectionArray = [EventSectionHeader]()
    

    Finally, the code to read that data in and populate the dataSource.

    func getEventsFromFirebaseDB() {
        //var eventArray = [EventInfo]()
        let eventRef = Database.database().reference().child("events")
        eventRef.observeSingleEvent(of: .value, with: { snapshot in
            let allEventSnaps = snapshot.children.allObjects as! [DataSnapshot]
            for eventSnap in allEventSnaps {
                let event = EventInfo(eventSnap: eventSnap)
                
                if let foundEventSection = self.eventSectionArray.first(where: { $0.eventDate == event.date } ) {
                    foundEventSection.eventArray.append(event) //found same date, add event
                } else { //eventSection with this date doesn't exist, add a new section with this date and add this
                         //  event to that new section
                    let eventSection = EventSectionHeader()
                    eventSection.eventDate = event.date
                    self.eventSectionArray.append(eventSection)
                }
            }
            
            self.tableView.reloadData()
        })
     }
    

    The above code reads in all of the events once and does not leave an observer. It iterates over all of the events in the snapshot, creating EventObjects for each and checking to see if any Event Dates (the section) already exist.

    If so, the event it added to that sections date array.

    If not, a new EventSectionHeader is created, date is set and the event is added.