问题
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)
})
}
回答1:
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.
来源:https://stackoverflow.com/questions/54958223/childadded-observer-doesnt-get-called-if-the-function-is-not-called-explicitly