For a few days I have been trying to read my data from firebase without success.
Indeed it is a set of tables also containing tables.
This function is to retrieve the subjects and at the same time the paragraphs
func getSubjects() {
let subjectRef = database.child("subjects")
subjectRef.observe(.childAdded, with: { (snapshot) in
for child in snapshot.children {
print(snapshot)
if let snapshot = child as? DataSnapshot,
let subject = Subject(snapshot.value)
//subjectList.append(subject)
// print("Data : \(subject)")
}
})
}
This is the firebase screen
Console screen
On Android I didn't have this problem, but since I'm new to iOS, I'm having a hard time coping.
Any help will be welcome. Thank you
At the moment, you are observing the database for constant changes and it will only run when a child/value has been added into the place you're currently checking, for this you may only want to retrieve a value once, and every time that view is loaded then it will fetch from the database again. It's a lot more efficient and less costly. You may want something like this:
ref = Database.database().reference()
ref.child("subjects").child("0").child("paragraphs").child("0").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let location = value["location"] as? NSDictionary
let title= value?["title"] as? String ?? ""
let text = value?["text"] as? String ?? ""
let latitude = location?["latitude"] as? String ?? ""
let longitude = location?["longitude "] as? String ?? ""
}) { (error) in
print(error.localizedDescription)
}
You think each child with nodes inside it as an array, or a json object. You can cast them into an NSDictionary and use that cast to access values inside them if they're nested.
If they're not nested and in the same level as the place you're watching in the database ref, like for instance, above we are looking in the subjects > 0 > paragraphs > 0 node within the database. Title is a value inside that node and not a child so we can simply get the value of title from the database through the data snapshot given back.
I recommend reading the Docs, they're very good and easy to understand when working with different OS/languages.
Related
I'm trying to fetch the first key from my firebase database but for some reason nothing is being printed out. How can I get the first key from my firebase database using .childAdded
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
print(first)
This in incredibly easy if you literally are asking how to only ever get the first child of a node. Here's how to only get the first child of a /users node
func getFirstChild() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .childAdded, with: { snapshot in
print(snapshot)
})
}
or
print(snapshot.key)
if you just want the key.
Or, if you want to use a query to do the same thing
func getFirstChildAgain() {
let usersRef = self.ref.child("users")
let query = usersRef.queryOrderedByKey().queryLimited(toFirst: 1)
query.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
The child_added event is typically used when retrieving a list of items from the database. Unlike value which returns the entire contents of the location, child_added is triggered once for each existing child and then again every time a new child is added to the specified path. The event callback is passed a snapshot containing the new child's data. For ordering purposes, it is also passed a second argument containing the key of the previous child.
From: Read and Write Data on iOS
Per your requirements, this is possible in .value and childAdded.
var child_array = [String:String]
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as? String ?? "Empty String"
let key = snapshot.key
child_array[key] = value;
}) { (error) in
print(error.localizedDescription)
}
then:
if let first = child_array.first?.key {
print(first) // First Key
}
Big NOTE: child_added randomly collects this data, you should never use it to sort your data
This is my database design.
foodie-ab2b4{
Foods{
0{
FoodName: "Baked Beans In Tomato Sauce"
FoodRecipe:
FoodUri:
Image:
}
1{
FoodName: "Another bean | Bubbling Bacon Butter Beans recipes"
FoodRecipe:
FoodUri:
Image:
}
}
}
I'm working on an ios project and this is how my firebase json structured.
let ref = Database.database().reference()
func searchFoodByName(FoodName: String){
let foodsRef = ref.child("Foods")
let input = FoodName
let query = foodsRef.child(key).queryOrdered(byChild: "FoodName").queryEnding(atValue: input)
query.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let fName = dict["FoodName"] as! String
let fIngredients = dict["Ingredients"] as! [String]
print(fName)
print(fIngredients)
let key = snapshot.key
print(key)
}
})
}
I'm trying to filter my food objects by their names. I have 10 objects in my database. In most cases this search return true objects. But there is one example i can not solve.
When i query the "tomato" word i need to get 1 object. But there is two. Although there is no tomato word in my second object.
The two object is in my json file.
If it is necessary i can upload my whole JSON file to here.
You seem to assume that Firebase can filter based on strings that contain a value, but it can't. See Firebase query - Find item with child that contains string (and the many links from there).
What Firebase can do is search for string values that start with a certain value. You do that by using a combination of queryStarting(atValue:) and queryEnding(atValue:):
let query = foodsRef
.queryOrdered(byChild: "FoodName")
.queryStarting(atValue: input)
.queryEnding(atValue: input+"\\uf8ff")
If you use this query, and input is ``Baked`, it will only match the first food from your JSON.
I am trying to do a conditional within Firebase Observer.
Essentially I like to check if seat is occupied.
If it is, then orders can be retrieved.
If not then send the restaurant back to the search seat page again.
For some reason, the code within if !taken is never executed even if the condition is met (ie. the owner has inputted the wrong seat number). I have put it within the closure, it should run right?
func retrieveData (){
var taken = false
var seatNumber = "**an Int from other page**"
let refCustomer = Database.database().reference().child("Restaurant").child("Customers")
refCustomer.queryOrdered(byChild: "Seat").queryEqual(toValue: "\(seatNumber)").observeSingleEvent(of: .childAdded, with: { (snapshot) in
if snapshot.exists() {
taken = true
let snapshotValue = snapshot.value as? [String : AnyObject] ?? [:]
self.customerFirstName = snapshotValue["Firstname"] as! String
self.customerLastName = snapshotValue["Lastname"] as! String
self.customerAllergy = snapshotValue["Allergy"] as! String
self.customerID = snapshot.key
self.allergy.text = self.customerAllergy
self.ptname.text = "\(self.customerFirstName) \(self.customerLastName)"
}
if !taken {
print ("oops")
self.performSegue(withIdentifier: "MainPage", sender: self)
}
})
}
There are a number of issues with this code, and possibly your structure so let me see if I can point you in the right direction.
First, you can eliminate the taken varable as it's unneeded. In short
if snapshot.exists() {
//handle the snapshot
} else { //use else here as if the snapshot doesn't exist we want this code to run
print ("oops")
}
Second, ensure your structure is like this
Customers
cust_0
Seat: 1
cust_1
Seat: 2
etc
Third, this is a string of "1"
queryEqual(toValue: "\(seatNumber)")
and you want to query for an Int so make it
queryEqual(toValue: seatNumber)
which queries for an Int of 1
Forth:
When querying Firebase, closures will not execute when .childAdded doesn't find anything. You should use .value.
From the docs of .childAdded
This event is triggered once for each existing child and then again
every time a new child is added to the specified path.
So if no child nodes match the query, it will not execute.
Use this
refCustomer.queryOrdered(byChild: "Seat")
.queryEqual(toValue: seatNumber)
.observeSingleEvent(of: .value, with: { (snapshot) in
And... this is the important part, .value retrieves all nodes that match the query so you will need to iterate over those nodes to work with the child nodes. Assuming there would only ever be one match then you can do this
guard let allChildren = snapshot.children.allObjects as? [DataSnapshot] else {return}
let firstChild = allChildren.first
Fifth. While technically this is ok
let f = snapshotValue["Firstname"] as! String
You are guaranteeing that a Firstname node always exists. If that's true go with it. However, a safer, more Swifty way would be to do this
let f = snapshotValue["Firstname"] as? String ?? "No First Name"
I'm trying to retrieve data from Firebase but I'm having issues.
let refEmployees = Database.database().reference(withPath: "Employees")
refEmployees.child("Logan").observeSingleEvent(of: .value, with: {
snapshot in
let shift = snapshot.value as? String
self.shifts.append(shift!)
self.workSchedule.reloadData()
})
That is my code, my database looks like this.
When I run it, I get
fatal error: unexpectedly found nil while unwrapping an Optional value
Any ideas? I'm stumped. I also can add information to the database at the same time and change to childAdded and the information loads properly.
Usually I never try to get the reference "withPath", but by child.
I would Try to do it like this:
let ref = Database.database().reference().child("Employees").child("Logan")
On the other part, is a bad practice to force unwrap optionals that you don't know you are actually going to get, I would change the closure to:
ref.observeSingleEvent(of: .value, with: {
snapshot in
guard let shift = snapshot.value as? String else {
print("Value is Nil or not String")
return
}
self.shifts.append(shift)
self.workSchedule.reloadData()
})
Like someone in the comments said, may be a good idea to print the optional value first to see if you are actually getting the value that you need from the database.
Hope this helps
let refEmployees = Data`base.database().reference()
refEmployees,child("Employees").child("Logan").observeSingleEvent(of: .value, with: {
snapshot in
let shift = snapshot.value! as! [String : AnyObject]
let dictKeys = [String](snapshot.keys)
//Will give you all` keys from DB 0,1,2,3,4,5,6
let dictValues = [AnyObject](snapshot.values)
//will give you` all values "off", "off", "2-10"..."off"
//use keys or values append in array if you want as I could not understand what you are trying to save in self.shifts
self.shifts.append(shift!)
self.workSchedule.reloadData()
})
I'm new to firebase and I have such structure of my firebase project
I want to get all objects, that "Interested" value is equal to "men"
I wrote such code, to get all object sorted by interes value:
let thisUserRef = URL_BASE.childByAppendingPath("profile")
thisUserRef.queryOrderedByChild("Interest")
.observeEventType(.Value, withBlock: { snapshot in
if let UserInterest = snapshot.value!["Interest"] as? String {
print (snapshot.key)
}
}
But I receive nil.
you need to loop through all the key-value profiles
if let allProfiles = snapshot.value as? [String:AnyObject] {
for (_,profile) in allProfiles {
print(profile);
let userInterest = profile["Interest"]
}
}
Here _ is the key that is in the format KYXA-random string and profile will be the element for that key.
Edit:
There is querying for child values as per the docs.
Try thisUserRef.queryOrderedByChild("Interest").equalTo("men") and then using the inner loop that i specified in the answer
This is a basic query in Firebase. (Updated for Swift 3, Firebase 4)
let profileRef = self.ref.child("profile")
profileRef.queryOrdered(byChild: "Interest").queryEqual(toValue: "men")
profileRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let dict = child as! [String: Any]
let name = dict["Name"] as! String
print(name)
}
})
The legacy documentation from Firebase really outlines how to work with queries: find it here
Legacy Firebase Queries
The new documentation is pretty thin.
Oh, just to point out the variable; thisUserNode should probably be profileRef as that's what you are actually query'ing.