I'm trying to pull values from my Firebase real-time database, but for whatever reason the values come back as NSNull. I was getting error 'Could not cast value of type 'NSNull' to 'NSNumber' on line 13 until I changed 'let count = ...' to an 'if let'. I'm not sure why these values are returning null when they are not.
I've already tried printing the retrieved snapshot, and, as I expected, none of the values are null. I am stumped. Here is the log I get when printing the retrieved snapshot:
[Snap (A12-1A) 0, Snap (B1-2A) 0, Snap (C2-3A) 0, Snap (D3-4A) 0, Snap (E4-5A) 0, Snap (F5-6A) 0, Snap (G6-7A) 0, Snap (H7-8A) 0, Snap (I8-9A) 0, Snap (J9-10A) 5, Snap (K10-11A) 0, Snap (L11-12P) 0, Snap (M12-1P) 0, Snap (N1-2P) 0, Snap (O2-3P) 0, Snap (P3-4P) 0, Snap (Q4-5P) 0, Snap (R5-6P) 0, Snap (S6-7P) 0, Snap (T7-8P) 0, Snap (U8-9P) 0, Snap (V9-10P) 0, Snap (W10-11P) 0, Snap (X11-12A) 0]
All of the data in this snapshot is the data in my Firebase database, so I'm not sure why I'm getting this error.
My firebase setup: calendar-signups --- 04132019 --- (A12-1A: 0, B1-2A: 0, ..., X11-12P: 0)
func getCalendarSignupsOnDate(selectedDate: String, handler: @escaping (_ dateSignups: [Calendar]) -> ()) {
var calendarArray = [Calendar]()
REF_CALENDAR_SIGNUPS.observeSingleEvent(of: .value) { (snapshot) in
guard let snapshot = snapshot.children.allObjects as? [DataSnapshot] else { return }
var doesContainDate = false
for date in snapshot {
if date.key == selectedDate {
doesContainDate = true
self.REF_CALENDAR_SIGNUPS.child(date.key).observeSingleEvent(of: .value, with: { (dateSnapshot) in
guard let dateSnapshot = dateSnapshot.children.allObjects as? [DataSnapshot] else { return }
for timeSlots in dateSnapshot {
let time = timeSlots.key
if let count = timeSlots.childSnapshot(forPath: "J9-10A").value as? Int {
let calendarData = Calendar(time: time, signupCount: count)
calendarArray.append(calendarData)
handler(calendarArray)
} else {
let count = 0
let calendarData = Calendar(time: time, signupCount: count)
calendarArray.append(calendarData)
handler(calendarArray)
}
}
})
}
}
for item in calendarArray {
print(item.time + " " + String(item.signupCount))
}
if doesContainDate == false {
let dateSignUpData = [
"A12-1A": 0,
"B1-2A": 0,
"C2-3A": 0,
"D3-4A": 0,
"E4-5A": 0,
"F5-6A": 0,
"G6-7A": 0,
"H7-8A": 0,
"I8-9A": 0,
"J9-10A": 0,
"K10-11A": 0,
"L11-12P": 0,
"M12-1P": 0,
"N1-2P": 0,
"O2-3P": 0,
"P3-4P": 0,
"Q4-5P": 0,
"R5-6P": 0,
"S6-7P": 0,
"T7-8P": 0,
"U8-9P": 0,
"V9-10P": 0,
"W10-11P": 0,
"X11-12A": 0
]
as [String : Int]
self.REF_CALENDAR_SIGNUPS.child(selectedDate).updateChildValues(dateSignUpData)
for timeSlots in dateSignUpData {
let time = timeSlots.key
let count = timeSlots.value
let calendarData = Calendar(time: time, signupCount: count)
calendarArray.append(calendarData)
}
}
handler(calendarArray)
}
}
This isn't really going to be a complete answer but maybe provide you the right direction. There are some confusing bits to the code so I'll review those and then possibly provide a better direction...
Your function is passed a single selected date
func getCalendarSignupsOnDate(selectedDate: String
which means you know the date node you're trying to read. However, the code is reading in ALL the dates and iterating over them trying to find that date
for date in snapshot {
if date.key == selectedDate {
It would probably be a lot less code to just directly read that node, then work with the child nodes within it.
Also, further down in the code, you're iterating over the child nodes of A12-1A, B1-2A, looking for specific node
if let count = timeSlots.childSnapshot(forPath: "J9-10A").value as? Int
which won't work at all because timeSlots A12-1A etc have values, all 0, not child snapshots. The code continues and appears that you want to read the value of that specific child node and then set the rest to 0 which are added to your calendar array.
let count = 0
let calendarData = Calendar(time: time, signupCount: count)
But then farther down, if you don't find that timeslot, all of the child nodes are set to 0 anyway.
if doesContainDate == false {
let dateSignUpData = [
"A12-1A": 0,
"B1-2A": 0,
So... I think I get the gist of what you're trying to do. Here's some code that may get you going the right direction. This should replace all of the code in your question.
func getCalendarSignupsOnDate(selectedDate: String) {
var dateSignUpData = [
"A12-1A": 0,
"B1-2A": 0,
"C2-3A": 0,
"J9-10A": 0
]
let slotYourLookingFor = "J9-10A"
let dateRef = self.REF_CALENDAR_SIGNUPS.child(selectedDate).child(slotYourLookingFor)
dateRef.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
let count = snapshot.value as? Int ?? 0
dateSignUpData[slotYourLookingFor] = count //update this array element with the new count from Firebaes
} else { //didn't find the child node at all, will create the date node and add child nodes with val = 0
self.REF_CALENDAR_SIGNUPS.child(selectedDate).updateChildValues(dateSignUpData)
}
//do something with the dateSignUpData array
})
}
Explanation:
Pass in the date you are interested in. The code will read that node with child node of "J9-10A", the one you are looking for.
If it exists, it will read that data and update the dateSignUpData array with the count from that child node. You can then process the updated array further.
If the "J9-10A" node does not exist, a node with the date passed in will be created, and then all child node slots will be set to 0. You can then process the array further.
Last thing. Probably a good idea to store dates as yyyymmdd for ordering.