Right now I have .observeSingleEvent(of: .value) that loads all my annotations onto the map:
func loadAnnotations() {
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: { (snapshot) in
for item in snapshot.children {
// annotationListItem is a struct I created
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
}, withCancel: nil)
}
}
Now I want the map to update every time the user adds a new annotation so I put in the exact same code but with .observe(.childAdded)
func annotationChildAdded() {
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observe(.childAdded, with: { (snapshot) in
for item in snapshot.children {
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
}, withCancel: nil)
}
}
I get the error of:
Could not cast value of type '__NSCFString' (0x1060b84f0) to 'NSDictionary' (0x1060b92d8).
Printing description of snapshotValue:
([String : AnyObject]) snapshotValue = variable not available>
How can I fix this problem?
UPDATE
.observe(.value) works. But I am still wondering why .childAdded doesn't
The problem here is that .observeSingleEvent(of: .value) and .observe(.childAdded) don't return the same thing.
When you call .observe(.value) it returns the entire node with all the content inside at the time of the event.
But, when you use .observe(.childAdded) it would return only the content that is added to the specified path (that is, the child that is added).
You can see that by doing print(snapshot) in both methods and you will easily see the difference.
So, to access your data using .childAdded, you don't need to iterate through all the children like you would do with .observeSingleEvent(of: .value). Instead you would do :
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { (snapshot) in
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
let doubleLatitude = Double(annotationItem.mapViewLatitude!)
let doubleLongitude = Double(annotationItem.mapViewLongitude!)
let coordinate = CLLocationCoordinate2DMake(doubleLatitude!, doubleLongitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = annotationItem.annotationTitle
annotation.subtitle = annotationItem.annotationSubtitle
self.mapView.addAnnotation(annotation)
}
})
Also, I would not recommend you to force cast your item like that item as! DataSnapshot because if you miss something in your database the app would just crash.
Instead, I would do it like so, using guard statements :
guard let uid = Auth.auth().currentUser?.uid else {
return
}
uidRef.child(uid).child("annotations").observe(.childAdded, with: { [weak self] (snapshot) in
let annotationKey = snapshot.key
guard let longitude = snapshot.childSnapshot(forPath: "longitude").value as? NSNumber else { return }
guard let latitude = snapshot.childSnapshot(forPath: "latitude").value as? NSNumber else { return }
guard let title = snapshot.childSnapshot(forPath: "title").value as? String else { return }
// Here it really depends on how your database look like.
// ...
// Create your annotation item using the values from the snapshot :
let annotation = AnnotationListItem(id: annotationKey, title: title, longitude: longitue, latitude: latitude)
// Finally add the data to an array :
self?.annotationsArray.append(annotation)
// Or directly to your mapView :
self?.mapView.addAnnotation(annotation)
})
Just let me know if it helps ;D
Update
For example, let's say you have initially 3 child nodes in your database. It would do something like that :
initial : observer get called :
you get child 1
you get child 2
you get child 3
now, if another child is added :
new child : observer get called :
you get child 4
etc..
Related
I have a function that should listen to a Firebase node and get a snapshot of new posts when they get posted, but the function is not getting galled at all, as if the observer .observe(DataEventType.childAdded, with: { (snapshot) in didn't see new posts in the node. I checked and new posts are indeed registered in real time in Firebase. Should I call the function or is the observer that should do it?
Heres the complete function:
func getNewerAlerts(setCompletion: #escaping (Bool) -> ()) {
print(" MapArray.alertNotificationCoordinatesArray before getNewerAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray before getNewerAlerts snapshot is: \(self.userAlertNotificationArray)")
ref = Database.database().reference()
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(DataEventType.childAdded, with: { (snapshot) in
print(" snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String:[String:String]] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
data.values.forEach {
let dataLatitude = $0["Latitude"]!
let dataLongitude = $0["Longitude"]!
let type = $0["Description"]!
let id = Int($0["Id"]!)
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
// print("Firebase alerts posts retrieved")
let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)
self.mapView.addAnnotation(userAlertAnnotation)
self.userAlertNotificationArray.append(userAlertAnnotation)
MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate)
}
print(" MapArray.alertNotificationCoordinatesArray after getNewerAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray after getNewerAlerts snapshot is: \(self.userAlertNotificationArray)")
setCompletion(true)
})
}
Thank you very much.
EDIT
Rewritten function :
func getAlerts(setCompletion: #escaping (Bool) -> ()) {
self.mapView.removeAnnotations(mapView.annotations)
MapArray.alertNotificationCoordinatesArray.removeAll()
MapArray.userAlertNotificationArray.removeAll()
print(" MapArray.alertNotificationCoordinatesArray before getNewerAlerts is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray before getNewerAlerts is: \(MapArray.userAlertNotificationArray)")
ref = Database.database().reference()
// ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(DataEventType.childAdded, with: { (snapshot) in
// self.mapView.removeAnnotations(self.mapView.annotations) //
print(" added snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String:String] else { return }
// guard let firebaseKey = snapshot.key as? String else { return }
let firebaseKey = snapshot.key
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
let type = data["Description"]!
let id = Int(data["Id"]!)
let userName = data["user"]!
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!, userName: userName)
MapArray.userAlertNotificationArray.append(userAlertAnnotation) // array of notifications coming from Firebase
MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate) // array for checkig alerts on route
print(" MapArray.alertNotificationCoordinatesArray after getNewerAlerts is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray after getNewerAlerts is: \(MapArray.userAlertNotificationArray)")
setCompletion(true)
self.mapView.addAnnotations(MapArray.userAlertNotificationArray)
})
// ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(.childRemoved, with: { (snapshot) in
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(DataEventType.childRemoved, with: { (snapshot) in
print(" self.userAlertNotificationArray before getDeletedAlerts snapshot is: \(MapArray.userAlertNotificationArray)")
print(" MapArray.alertNotificationCoordinatesArray before getDeletedAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" removed snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String:String] else { return }
let firebaseKey = snapshot.key
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
let type = data["Description"]!
let id = Int(data["Id"]!)
let userName = data["user"]!
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
_ = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!, userName: userName)
MapArray.userAlertNotificationArray.removeAll(where: { ($0.firebaseKey == firebaseKey) }) //remove the alert
MapArray.alertNotificationCoordinatesArray.removeAll(where: { ($0.latitude == recombinedCoordinate.latitude && $0.longitude == recombinedCoordinate.longitude) })
self.mapView.removeAnnotations(self.mapView.annotations)
self.mapView.addAnnotations(MapArray.userAlertNotificationArray)
print(" self.userAlertNotificationArray after getDeletedAlerts snapshot is: \(MapArray.userAlertNotificationArray)")
print(" MapArray.alertNotificationCoordinatesArray after getDeletedAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
setCompletion(true)
})
}
Instead of a lengthy discussion in comments let's try to answer this with a couple of links and a code example.
First though, you should only synchronize data when a view is visible and the viewWillAppear method is called each time the view becomes visible, so that's a good place to add observers. It's also good practice to remove observers when you don't need them (saves bandwidth) and that can be done using a firebase handle in the viewDidDisappear. Here's a slightly dated article but a great read
Best Practices for UIViewController and Firebase
and for a great example, see the answer to this question
Firebase: when to call removeObserverWithHandle in swift
To address the rest of the question (note I am keeping this short so I didn't include using handles)
I have a class to store alerts
class AlertClass {
var node_key = ""
var msg = ""
init(aKey: String, aMsg: String) {
self.node_key = aKey
self.msg = aMsg
}
}
and then a class var array to store all of the alerts
var alertArray = [AlertClass]()
and then we add our observers from the viewWillAppear function
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.addObservers()
}
which adds three observers to the referenced node; .childAdded, .childChanged, and .childRemoved. Keep in mind that .childAdded will iterate over the nodes in the ref node and populate our dataSource any time viewWillAppear is called, so we need to 'reset' the array so we don't accidentally load data on top of the existing data. Your use case may differ so code accordingly.
Here's the code to add the observers and prints the array any time there's a change.
func addObservers() {
let ref = self.ref.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications")
self.alertArray = []
ref.observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
let msg = snapshot.childSnapshot(forPath: "msg").value as! String
let aAlert = AlertClass(aKey: key, aMsg: msg)
self.alertArray.append(aAlert) //append the new alert
self.showAlertArray() //this is called for every child
})
ref.observe(.childChanged, with: { (snapshot) in
let key = snapshot.key
let msg = snapshot.childSnapshot(forPath: "msg").value as! String
if let foundAlert = self.alertArray.first(where: { $0.node_key == key } ) {
foundAlert.msg = msg //update the alert msg
self.showAlertArray()
}
})
ref.observe(.childRemoved, with: { (snapshot) in
let key = snapshot.key
self.alertArray.removeAll(where: { $0.node_key == key }) //remove the alert
self.showAlertArray()
})
}
func showAlertArray() {
for alert in self.alertArray {
print(alert.node_key, alert.msg)
}
}
and as a side note...
If you're populating a tableView dataSource via the childAdded you may be wondering how to do that without calling tableView.reloadData repeatedly, which may cause flicker. There's a techniqure for doing that by leveraging the fact that .value events are called after .childAdded. See my answer to this question for an example.
I'm rewriting my firebase functions to put in it's own function the realtime part of getting data fro database, so I changed from .observe(.childAdded, with: {(snapshot) in to .observeSingleEvent(of: .value, with: { (snapshot) in in referencing to database. The function now is returning on guard let data = snapshot.value as? [String :String] else { return }when it wasn't before..
What has changed when snapshot is the same? Any explanation of it wold be great as I don't see it myself.
Many thanks as always.
Here are the two versions of the function:
Old observer:
func displayAlerts(setCompletion: #escaping (Bool) -> ()) {
self.mapView.removeAnnotations(mapView.annotations)
MapArray.alertNotificationCoordinatesArray.removeAll()
self.userAlertNotificationArray.removeAll()
print(" MapArray.alertNotificationCoordinatesArray before snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray before snapshot is: \(self.userAlertNotificationArray)")
ref = Database.database().reference()
databaseHandle = ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
// self.mapView.removeAnnotations(self.mapView.annotations) //
print(" snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String:String] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
let type = data["Description"]!
let id = Int(data["Id"]!)
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase alerts posts retrieved")
let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)
self.userAlertNotificationArray.append(userAlertAnnotation) // array of notifications coming from Firebase
// print("userAlertNotificationArray after retrieving from Firebase is : \(self.userAlertNotificationArray)")
MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate) // array for checkig alerts on route
print(" MapArray.alertNotificationCoordinatesArray after snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray after snapshot is: \(self.userAlertNotificationArray)")
setCompletion(true)
self.mapView.addAnnotations(self.userAlertNotificationArray)
})
}
New observer:
func displayAlerts(setCompletion: #escaping (Bool) -> ()) {
print(" MapArray.alertNotificationCoordinatesArray before newer displayAlert snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray before displayAlert snapshot is: \(self.userAlertNotificationArray)")
if self.userAlertNotificationArray.count == 0 {
ref = Database.database().reference()
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observeSingleEvent(of: .value, with: { (snapshot) in
// self.mapView.removeAnnotations(self.mapView.annotations) //
print(" snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String :Any] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"] as! Double
let dataLongitude = data["Longitude"] as! Double
let type = data["Description"] as! String
let id = Int(data["Id"] as! String)
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude, longitude: doubledLongitude)
print("Firebase alerts posts retrieved")
let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)
self.userAlertNotificationArray.append(userAlertAnnotation) // array of notifications coming from Firebase
MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate) // array for checkig alerts on route
print(" MapArray.alertNotificationCoordinatesArray after newer displayAlert snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray after newer displayAlert snapshot is: \(self.userAlertNotificationArray)")
self.mapView.addAnnotations(self.userAlertNotificationArray)
setCompletion(true)
})
}
}
EDIT:
here are the prints from snapshots so to see results of the two versions:
observeSingleEvent snapshot:
snapshot is: Snap (Alert Notifications) {
"-LZtTuFSKMhhXFwyT-7K" = {
Description = "Ciclabile chiusa";
Id = 0;
Latitude = "44.50139187990401";
Longitude = "11.33592981426992";
};
"-LZtUV8MOxVrvPnEfi4g" = {
Description = "Lavori in corso";
Id = 1;
Latitude = "44.5013918797401";
Longitude = "11.335929814371545";
};
"-LZtV7sJJrOQjAimszTm" = {
Description = "Pericoli sulla ciclabile";
Id = 2;
Latitude = "44.50139187974223";
Longitude = "11.335929814367324";
};
}
and the childAdded snapshots:
snapshot is: Snap (-LZtTuFSKMhhXFwyT-7K) {
Description = "Ciclabile chiusa";
Id = 0;
Latitude = "44.50139187990401";
Longitude = "11.33592981426992";
}
snapshot is: Snap (-LZtUV8MOxVrvPnEfi4g) {
Description = "Lavori in corso";
Id = 1;
Latitude = "44.5013918797401";
Longitude = "11.335929814371545";
}
snapshot is: Snap (-LZtV7sJJrOQjAimszTm) {
Description = "Pericoli sulla ciclabile";
Id = 2;
Latitude = "44.50139187974223";
Longitude = "11.335929814367324";
}
Change
guard let data = snapshot.value as? [String :String] else { return }
to
guard let data = snapshot.value as? [String :[String:String]] else { return }
data.values.forEach {
print($0["Latitude"])
print($0["Longitude"])
}
Can you show an exemple on how the json is beeing stored in database? with
.observe(.childAdded, with: {(snapshot) in
you receive only the child that was added, and with
.observeSingleEvent(of: .value, with: { (snapshot) in
you receive the whole node, so maybe the node is not [String:String] its probably more then one item so it should be [String:Any] and then you can get every child from within it.
This is just a partial answer but after lots of different tries and with the help of Sh_Khan we got to make it work. The problem was in the line guard let data = snapshot.value as? [String :Any] else { return }that needed to be guard let data = snapshot.value as? [String : [String : String] else { return }.
so the function got to be written as:
func displayAlerts(setCompletion: #escaping (Bool) -> ()) {
print(" MapArray.alertNotificationCoordinatesArray before displayAlert snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray before displayAlert snapshot is: \(self.userAlertNotificationArray)")
if self.userAlertNotificationArray.count == 0 {
ref = Database.database().reference()
ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observeSingleEvent(of: .value, with: { (snapshot) -> Void in
print(" snapshot is: \(snapshot)")
guard let data = snapshot.value as? [String :[String:String]] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
data.values.forEach {
// let firebaseKey = data.keys[]
let dataLatitude = $0["Latitude"]!
let dataLongitude = $0["Longitude"]!
let type = $0["Description"]!
let id = Int($0["Id"]!)
print("firebaseKey is:\(firebaseKey)")
print("dataLatitude is: \(dataLatitude)")
print("dataLongitude is: \(dataLongitude)")
print("type is: \(type)")
print("id is: \(id)")
print("Key is: \(firebaseKey)")
print("data is: \(data)")
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)
self.userAlertNotificationArray.append(userAlertAnnotation) // array of notifications coming from Firebase
MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate)
}
print("Firebase alerts posts retrieved")
print(" MapArray.alertNotificationCoordinatesArray after displayAlert snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
print(" self.userAlertNotificationArray after displayAlert snapshot is: \(self.userAlertNotificationArray)")
self.mapView.addAnnotations(self.userAlertNotificationArray)
setCompletion(true)
})
}
}
Now there are two things I'm left to resolve. First: How to get the child key as the line guard let firebaseKey = snapshot.key as? String else { return }gives me the name of the node instead?
Second: The newer function I created just to get newerAlerts with .observe(.childAdded, with: { (snapshot) in does not get called whe a new child is added to the node.
Should I ask new questions or continue here?
Thank you
I need a pretty simple thing and I cant figure it out. I created a snapshot of firebase, and i matched the userID with the snapshots name of inside a child. I just need the childs ID (which i created using childbyautoID)
here is my code:
func checkIfUserIsLoggedIn(){
if Auth.auth().currentUser?.uid == nil {
} else {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("Users").child(uid!).child("data").observeSingleEvent(of: .value, with: {(snapshot) in
if let dictionary = snapshot.value as? [String:AnyObject] {
self.fnamefinal = dictionary["email"] as? String
if self.fnamefinal != nil {
self.ref.child("Request").observe(.childAdded, with: { (snapshot) in
let results = snapshot.value as? [String : AnyObject]
let name = results?["name"]
let garage = results?["garage"]
let time = results?["time"]
let latitude = results?["latitude"]
let longitude = results?["longitude"]
let childID = results?[""]
print(snapshot)
print(childID as! String?)
if name as! String? == self.fnamefinal {
let myCalls = RidesRequestedd(name: name as! String?, garage: garage as! String?, time: time as! String?, latitude: latitude as! Double?, longitude: longitude as! Double?)
self.frequests1.append(myCalls)
self.rideslabel.text = myCalls.garage
} else {
}
})
} else {
print("no")
}
}
})
//}
}
Here is the snapshot of the matched name with user ID:
Snap (-LMBAF69-kYKnWoK2n9M) {
garage = "Coliseum/Bobcat Stadium";
latitude = "29.89";
longitude = "-97.953";
name = "test3#gmail.com";
time = "12:13 AM";
}
I just need the LMBAF69.... string. Simple but i cant figure it out
I have two observers, the second observer is dependent on the first observers value. I can't seem to get the first observer to work, I am not getting any errors on Xcode. The first function has to check the Users profile for information and then use that information to search for different information in the database. Here is my code:
func loadposts() {
ref = Database.database().reference()
let trace = Performance.startTrace(name: "test trace")
trace?.incrementCounter(named:"retry")
let userID = Auth.auth().currentUser?.uid
print(userID!)
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let one1 = value?["Coupon Book"] as? String ?? ""
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
}) { (error) in
print(error.localizedDescription)
}
delay(0.1) {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
Database.database().reference().child("Coupons").child(self.bogus.string(forKey: "bogus")!).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] as! String
let offerx = dict["offer count"] as! String
let logocomp = dict["logo"] as! String
let actchild = dict["childx"] as! String
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
self.tableview.reloadData()
print(self.posts)
}
}
}
trace?.stop()
}
Any help is appreciated.
self.bogus.string(forKey: "bogus"))" is nil because observeSingleEvent is an async method, so to get the required results you need to call the second observer inside the first observer or you can use the completion handler
You can use the completionHandler like this:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
func firstObserverMethod(completionCallback: #escaping () -> Void) {
ref.child("Users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
if let value = snapshot.value as? [String: Any] {
let one1 = value["Coupon Book"] as? String
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
completionCallback()
}
}) { (error) in
print(error.localizedDescription)
}
}
Now using the above method:
firstObserverMethod {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
guard let bogusString = self.bogus.string(forKey: "bogus") else {
print("bogus is not set properly")
return
}
Database.database().reference().child("Coupons").child(bogusString).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] ?? ""
let offerx = dict["offer count"] ?? ""
let logocomp = dict["logo"] ?? ""
let actchild = dict["childx"] ?? ""
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
DispatchQueue.main.async {
self.tableview.reloadData()
}
print(self.posts)
}
}
}
Note: You should use optional binding to get the values from optional
Since you are using the result of the 1st observer in the reference of your 2nd observer, it's a very bad idea to add the 2nd observer right below the first observer. And adding a delay won't be a viable solution : these two calls are asynchronous, which means that the reason why you are not getting might very likely be because the 2nd observer is triggered even before the 1st has returned any data.
The solution here, would be using a completion handler, or you could just incorporate your 2nd observer inside the completion block of the 1st, to be make sure that the proper order (1st observer -> 2nd observer) will always be respected.
It would look somehow like this:
func loadposts() {
// ...
// 1st Observer here
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get your value here
guard let one1 = snapshot.childSnapshot(forPath: "Coupon Book").value as? String else { return }
// 2nd Observer here. Now you can use one1 safely:
Database.database().reference().child("Coupons").child(one1).observe(.childAdded) { (Snapshot : DataSnapshot) in
// ...
}
})
}
Now, a couple of things that you could also improve in your code, while not directly related to the question:
I would suggest you to make use of guard statements instead force-unwrapping, which may end up in crashing your app at some point.
For example, you could check whether your current user exist or not like so:
guard let currentUserID = Auth.auth().currentUser?.uid else {
return
}
// Now you can use safely currentUserID
Also, when you try to get the data out of the snapshot, it's not a good idea either, to use force-casting. You would better write it in this way:
yourRef.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
guard let text = child.childSnapshot(forPath: "text").value as? String, let somethingElse = child.childSnapshot(forPath: "otherValue").value as? NSNumber else {
return
}
// And so on, depending of course on what you have in your database.
}
Im trying to create a childSnapshot in firebase using for child in snapShot.children I know back then I used to be able to make it work, Im not sure if this was change in the new update
-old way that used to work
for child in snapshot.children {
let firUserId = child.childSnapshot(forPath: "userId").value! as! String
let receivePostSnapshot = child.childSnapshot(forPath: "receivePost")
}
my current code does not work
func displayAvailaibleHelprOnMap(){
let refUrl = "frontEnd/users"
let childRef = Ref.childRef(refUrl: refUrl)//.child("users")
childRef.observe(.value, with: {snapshot in
let dict = snapshot.value as? NSDictionary
for child in snapshot.children{
// I want to use child snapshot here
//let childDict = child.childSnapshot(forPath: "receivePost")
}
}
Try:
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("users").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots
{
let firUserId = child.childSnapshot(forPath: "userId").value! as! String
let receivePostSnapshot = child.childSnapshot(forPath: "receivePost")
}
}
})
try this
let mRef = mRestCallObj.getReferenceFirebase()
mRef!.child("users").queryOrderedByChild("receivePost").observeEventType(.ChildAdded, withBlock: { snapshot in
let userName = snapshot.value!["receivePost"] as? String
}
Try this-
let ref = FIRDatabase.database().reference().child("Users + infomation").child(userid).child("cats")`
ref.observeSingleEventOfType(.Value, withBlock : {(snapShot) in
if let val = snapShot.value as? Int {print(snapShot)}