value not appearing in the rest of my code - ios

I'm populating my "filteredLocations" array by using this:
let sampleRef = FIRDatabase.database().reference().child("SamplePost").child("post")
sampleRef.observeSingleEvent(of:.value, with: {(snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result{
let dictionary = child.value as? [String: AnyObject]
let lat = dictionary?["lat"] as! Double
let long = dictionary?["long"] as! Double
let structure = MapPoints(Latitude: lat, Longitude: long)
self.filteredLocations.append(structure)
print("This is the amount in here \(self.filteredLocations.count)")
}
}
})
the print statement within the my snapshot returns 2, but when I print filteredLocations.count anywhere else it returns 0. I have the Firebase code at the start of the viewdidload

Your problem is that "sampleRef.observeSingleEvent" is asynchronous. What this means is that it is run in a background thread waiting for data while the app continues executing functions like viewWillAppear etc on the main thread.
By the time you get data back from the server the other print count methods would have already been executed before the array was populated with data.
To get a better understanding of this. Place a UIButton on your controller and bind it to a function that prints the array count. Then start the app and press the button. It should print 2 as by the time you press the button you should have got data back from the server.

Related

Retrieving data from firebase Realtime Datbase with SwiftUI Xcode 11

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.

Getting conditionals to work within Firebase Observer

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"

How to save Firebase Data into an Array

I have a Firebase database that is modeled as such:
: users
: some-random-id-1
- username:"user1"
- email:"email1#gmail.com"
: some-random-id-2
- username:"user2"
- email:"email2#gmail.com"
I am trying to iterate through all the users in the dictionary of data and append the username into a list in the file to be used for other purposes. I created a string array (userslist) variable and in viewdidload(), I wrote the following code below:
ref = Database.database().reference()
ref?.observe(.value, with: { (snapshot) in
let dataDict = snapshot.value as! NSDictionary
let x = dataDict["users"] as! NSDictionary
print(x)
print("--------")
for user in x{
let y = user.value as? [String: String]
let z = y!["username"]
print(z)
self.userslist.append(z!)
print(self.userslist)
print("NEXT")
}
})
print(self.userslist)
Inside the brackets of the snapshot, when I print self.userslist, I can see that each element is getting added, but when I print it a final time outside of the brackets, it shows it as an empty array. I think that the elements are only appended in the scope of those brackets so I cant access the filled array anywhere else. How do I get around this so I can use the data I appended?
you are using print(self.userslist) outside the observer and Firebase run in Async Mode
So, if you make use of breakpoints you will notice that
print(self.userslist) is Called before the control reach onside the Database handler ,
data is getting fetched you need to load your views inside that handler using Dispatch main queue
ref?.observe(.value, with: { (snapshot) in
let dataDict = snapshot.value as! NSDictionary
let x = dataDict["users"] as! NSDictionary
print(x)
print("--------")
for user in x{
let y = user.value as? [String: String]
let z = y!["username"]
print(z)
self.userslist.append(z!)
print(self.userslist)
print("NEXT")
}
/// Here is your data
print(self.userslist)
})
/// Called before Handler execution
print(self.userslist)

(Swift + Firebase) Convert observeSingleEvent to observe .childAdded

I have a observeSingleEvent in my viewDidLoad function and I want to change it into a observe with .childAdded so that it will listen constantly and add the objects whenever they get added to the database.
Here's the code that reads from firebase:
let parentRef = Database.database().reference().child("Recipes")
parentRef.observeSingleEvent(of: .value, with: { snapshot in
// PROCESSES VALUES RECEIVED FROM SERVER
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
// DEFINE VARIABLES FOR LABELS
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
food.append(Element(name: recipeName!, description: recipeDescription!))
self.tableView.reloadData()
}
}
})
Another problem that I have now (besides that it only loads once) is that whenever I go to another view in the app and then come back it reads everything again so I get duplicates of everything in my tableView, will that still happen with the observe?
Edit:
Here is what my database looks like:
Recipes
-Kv7FAqgLtDrRoyGd-99
Description: "food description"
Name: "food name"
-KvBuzMUnIQXn8gpG2WL
Description: "food description2"
Name: "food name2"
-KvH6yYeJaThK7oP8xBj
Description: "food description3"
Name: "food name3"
Change observeSingleEvent to observe.
Empty food array whenever observing new values.
Reload your food array outside your for-loop so that you only reload whenever you have loaded all of your items into your array.
let parentRef = Database.database().reference().child("Recipes")
// 1. Change to observe
parentRef.observe(.value, with: { snapshot in
// PROCESSES VALUES RECEIVED FROM SERVER
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
// 2. Empty food array
self.food = []
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
// DEFINE VARIABLES FOR LABELS
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
food.append(Element(name: recipeName!, description: recipeDescription!))
}
// 3. reload tableview outside loop
self.tableView.reloadData()
}
})
By emptying your food array whenever observing new values you ensure to only show the values that are present in your database and thus never having repetitive elements.

queryEqualTo is being skipped over

i'm trying to set up a viewCount for my app, when I set the breakpoints up and go through the code it always skips past the queryOrdered and i'm not exactly sure why
func increaseViewCount(username: String, time: NSNumber){
guard let uid = Auth.auth().currentUser?.uid else{
return
}
let refOfUserName = Database.database().reference().child("Users").child(uid)
refOfUserName.observeSingleEvent(of: .value, with: {(snapshot) in
let dictionaryOfUser = snapshot.value as? [String: AnyObject]
// let currentUsersName = dictionaryOfUser?["username"] as? String
let currentUsersName = "hello"
if username == currentUsersName {
print("this is the same user")
}else{
let postRef = Database.database().reference().child("HistoryOfPosts").child("post")
postRef.queryOrdered(byChild: "post").queryEqual(toValue: time).observeSingleEvent(of: .childAdded, with: {(snapshotPost) in
print(snapshotPost.exists())
print(snapshotPost)
let valString = snapshotPost.value
let number = valString as! NSNumber
var value = number.intValue
value = value + 1
let values = ["viewCount": value] as [String:Any]
postRef.updateChildValues(values)
})
}
})
}
The data is loaded from the Firebase Database asynchronously. Instead of waiting for that loading to complete, the program continues with the statement after you attach the observer.
In this case that means that the code pretty much exits increaseViewCount() straight after it attaches the observer. Then once the data comes back from the Firebase servers, the code in your callback block is executed.
To get into the callback block, place a breakpoint on the first statement in that block.

Resources