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)
}
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.