Firebase returns nil when fetching data - ios

I had a problem with fetching data, seems to be working now, but instead of observing .childAdded, i need to observe value.
My Current code function is:
func fetchJobs() {
ref.child("jobposts").observe(.childAdded) { (snapshot) in
guard let dictionary = snapshot.value as? [String:Any] else { return}
var job = JobData()
job.title = (dictionary["title"] as! String) <-- Error in any of the job.*
job.company = (dictionary["company"] as! String)
job.city = (dictionary["city"] as! String)
job.salary = (dictionary["salary"] as! String)
job.creator = (dictionary["creator"] as! String)
self.jobs.append(job)
}
}
When i change .observe(.childAdded) to .observe(.value) i get an error :
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Any ideas what could cause this?
Using print(dictionary) prints all the documents, maybe i need to map them somehow? Thank you in advance

When you observe a .value event for a list of nodes, you need to loop over snapshot.children to get the individual nodes. From the documentation on listening for value events for lists:
_commentsRef.observe(.value) { snapshot in
for child in snapshot.children {
...
}
}
The ... is where your current code inside the .childAdded listener goes.

Related

Strange behaviour on Firebase query result SWIFT

I'm getting this error on the line let itemToAdd = snapshot.childSnapshot(forPath: "Shopa function that retrieves data from Firebase.
the output of the console in Could not cast value of type 'NSNull' (0x1118c8de0) to 'NSString' (0x10dda45d8)..
What I'm trying to do is to filter database ordering by one value
opening Timeand than get another value Shop Namefrom the returned entries in the snapshot.
here's the function:
func filterOpenShops(enterDoStuff: #escaping (Bool) -> ()) {
ref = Database.database().reference().child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times")
let query = ref?.queryOrdered(byChild: "Opening Time").queryStarting(atValue: openingTimeQueryStart).queryEnding(atValue: openingTimeQueryEnd)
query?.observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children {
// new modification
if childSnapshot is DataSnapshot {
let itemToAdd = snapshot.childSnapshot(forPath: "Shop Name").value as! String // gets the open shop from snapshot
self.availableShopsArray.append(itemToAdd)
print(snapshot.children)
print(" Open Shops are \(self.availableShopsArray)")
}
}
// still asynchronous part
enterDoStuff(true)
// call next cascade function filterClosedShops only when data
})
// Sychronous part
print("opening query start is \(openingTimeQueryStart) and opening query end is \(openingTimeQueryEnd)")
} // end of filterOpenShops()
EDIT:
I rewrote the function as:
func filterOpenShops(enterDoStuff: #escaping (Bool) -> ()) {
// get from Firebase snapshot all shops opening times into an array of tuples
//shopOpeningTimeArray:[(storeName: String, weekdayNumber: String, opening1: Sring, closing1: String, opening2:String, closing2: String)]
ref = Database.database().reference().child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times")
let query = ref?.queryOrdered(byChild: "Opening Time").queryStarting(atValue: String(describing: openingTimeQueryStart)).queryEnding(atValue: String(describing :openingTimeQueryEnd))
query?.observe(.value, with: { (snapshot) in // original is ok
// guard let data = snapshot.value as? [String:String] else { return }
for childSnapshot in snapshot.children {
print("snapshot is: \(childSnapshot)")
print("snapshot.childrend is: \(snapshot.children)")
guard let data = snapshot.value as? [String:String] else { return }
let itemToAdd = data["Shop Name"]
self.availableShopsArray.append(itemToAdd!)
print("Open Shop is: \(String(describing: itemToAdd))")
print(" Open Shops are \(self.availableShopsArray)")
}
// still asynchronous part
enterDoStuff(true)
// call next cascade function filterClosedShops only when data
print(" Open Shops are \(self.availableShopsArray)")
})
print("opening query start is \(openingTimeQueryStart) and opening query end is \(openingTimeQueryEnd)")
} // end of filterOpenShops()
but I still get a null object and not a [String:String] as expected.
The function that created the entries in Firebase is:
func postOpeningTime() {
// if shopNameTextfield.text != nil && openingTimeTextfield.text != nil && closingTimeTextfield.text != nil {
let shopName = shopNameTextfield.text!
let openingTime = openingTimeTextfield.text!
let closingTime = closingTimeTextfield.text!
// } else {return}
let post: [String:String] = [
"Shop Name" : shopName ,
"Opening Time" : openingTime ,
"Closing Time" : closingTime
]
var ref: DatabaseReference!
ref = Database.database().reference()
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times").childByAutoId().setValue(post)
}
Now I have two behaviours:
1st: When querying for entries and finds values that are Int: completion get called but I get no snapshot print.
2nd: When querying for entries and find values that are String: completion doesn't get called but snapshot prints the right entries with values.
Can anyone please spot what's going on here?
I found the problem to bee the way I was casting query result.
Casting it as [String:String] produced to return because upshot was actually [String[String:String]] when all the values for entry's parameter were String, but as I changed Opening Time and Closing time to be Int, than I have to read the snapshot as [String[String:Any]].
So the final function is:
func filterOpenShops(setCompletion: #escaping (Bool) -> ()) {
// Empty the array for beginning of the search
self.availableShopsArray.removeAll()
var ref = Database.database().reference()
ref.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Shops").child("Shops Opening Times").queryOrdered(byChild: "Opening Time").queryStarting(atValue: openingTimeQueryStart).queryEnding(atValue: openingTimeQueryEnd).observe(.value) { (snapshot) in
print(snapshot)
if let data = snapshot.value as? [String : [String : Any]] {
for (_, value) in
data {
let shopName = value["Shop Name"] as! String
let active = value["Active"] as! String
if active == "true" {
self.availableShopsArray.append(shopName)
print("Shop_Name is :\(shopName)")
print("self.availableShopsArray is: \(self.availableShopsArray)")
}
}
} else {
print("No Shops")
}
// still asynchronous part
setCompletion(true)
// call next cascade function filterClosedShops only when data retrieving is finished
self.filterClosedShops(setCompletion: self.completionSetter)
print(" 1 Open Shops are \(self.availableShopsArray)")
}
} // end of filterOpenShops()

How can i get only keys from firebase?

I have a structure of database as on image and I need to display this date which is in the red rectangle. I tried to do smth like this, but it throws an error and I couldn't find same questions on a stack.
my database
reference.child("doc1").observe(.value, with: { (snapshot) in
if snapshot.exists() {
for date in (snapshot.value?.allKeys)
}
Your structure is a Dictionary of Dictionary so you have to cast your snap to [String:[String:Any]] where the key is your "11dot..." and value contains all hours
So try to use this code:
guard let dict = snap.value as? [String:[String:Any]] else { return }
for (key, value) in dict {
for (key2, value2) in value {
print(key2, value2) // this print your hours
}
}
Anyway I suggest you to don't use a observe(.value) which will read all change happened on all child node. Instead use the .childAdded feature of observer.
With a .childAdded you will receive only one child at a time (like a for on child node) and after that only the child added:
Database.database().reference().child("doc1").observe(.childAdded) { (snap) in
guard let dict = snap.value as? [String:Any]
print(dict) // this print data contains on "11dot10" and so on
}

Read values from database into Array

I'm trying to read from the database and place the values into an array of strings. However, when I try to push the values into an array then print the array the app crashes.
var pets: [String]?
override func viewDidLoad() {
super.viewDidLoad()
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let snap = snapshot.value as? Bool {
print("no values")
} else if let snap = snapshot.value as? NSDictionary {
for value in snap {
print(value.key as! String) // Prints out data in the database
self.pets?.append(value.key as! String)
}
print(self.pets!)
}
})
Does anybody know why the print(value.key as! String) prints out the data but then when I print out the array the app crashes with unexpectedly found nil while unwrapping an Optional value?
You never initialize pets. You declare it as an optional but never assign it a value. Why not change your code to the following:
var pets = [String]() // start with an empty array
override func viewDidLoad() {
super.viewDidLoad()
let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")
userRef.observeSingleEvent(of: .value, with: { snapshot in
if let snap = snapshot.value as? Bool {
print("no values")
} else if let snap = snapshot.value as? NSDictionary {
for value in snap {
print(value.key as! String) // Prints out data in the database
self.pets.append(value.key as! String)
}
print(self.pets)
}
})
Your array is nil when you try to force-unwrapping using:
print(self.pets!)
As you are using self.pets?.append() you don't have any problem because you are using the chaining of optionals, but your array, in fact, is nil at that time because you forgot to initialize it before use it. If you instead use self.pets!.append() you will see a runtime error instead.
So as #rmaddy suggest you can initialize the array at the beginning or just inside your viewDidLoad() it's up to you.
I hope this help you.

Could not cast value of type 'NSTaggedPointerString' to 'NSDictionary'. Swift/Firebase

When listening to a new object in Firebase, I get this error: Could not cast value of type NSTaggedPointerString to NSDictionary. Here is the code that listens to a a user that has registered.
FIRAuth.auth()!.addStateDidChangeListener { auth, user in
guard let user = user else { return }
self.user = User(authData: user)
let userref = FIRDatabase.database().reference(withPath: "users").child(self.user.uid)
userref.observe(.value, with: { snapshot in
print(snapshot.value!)
var newItems: [UserItem] = []
for item in snapshot.children {
let userDetail = UserItem(snapshot: item as! FIRDataSnapshot) /////////// CRASHES HERE //////////
newItems.append(userDetail)
}
self.userItem = newItems
})
print(self.user.uid)
}
And when the error comes up it points to here:
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String:Any] ///// ERROR HERE
name = snapshotValue["name"] as! String
email = snapshotValue["email"] as! String
age = snapshotValue["age"] as! String
ref = snapshot.ref
}
The console is printing what is inside Firebase correctly and the data is there and it exists, but this casting or data read error keeps happening. It has something to do with the conversion but I'm not exactly sure.
Say
snapshot.value as! String
In this case, the value is a string, so that is what you must cast to.
it might be the case that you updated your database and the old data did not have the correct format, try deleting your database, add new data and run your program again

Firebase with Swift ambiguous use of observeEventType

I've been pulling my hair out because of this. Going to all the pages with related incidents and multiple tutorials I find nothing wrong with my code here, but somehow it only doesn't fail if I print out the values (which works) or assign them as! NSArray which then gives me an empty array.
a print of snapshot.value shows
( friend1,
friend2,
friend3
)
I've tried snapshot.value.values ... no dice.
I've tried playing with the unwrapping ... no dice.
and this is my final attempt:
friendsList = [String]()
ref.observeSingleEventOfType(.Value) { (snapshot) -> Void in
if snapshot.value is NSNull {
} else {
for child in snapshot {
self.friendsList.append(child.value)
}
}
}
which gives me the ambiguous thing again.
Just some coding errors
Remove: (snapshot)-> Void
Change: child in snapshot as snapshot is not a sequence, whereas snapshot.children is
I assume you want to store the friends name as a string and name is a key in your structure. So change self.friendsList.append(child.value) to
let name = child.value["name"] as? String
friendsList.append(name!)
Here's the corrected code:
var friendsList = [String]()
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
} else {
for child in snapshot.children {
let name = child.value["name"] as? String
friendsList.append(name!)
}
print("\(friendsList)")
}
})
ref.observeSingleEvent(of: .value, with: { (snapshot) -> Void in
})
Work for me, Xcode 8.3.2

Resources