What does a Firebase observer actually do?
The Firebase client keeps an open socket connection to its backend server. When you attach the observer, it sends that information to the server, which will from then on track the observed location. The server also sends back the current value (or lack thereof) on the location. The client will immediately fire/invoke your completion block with that value.
After this initial flow, whenever the value at the location changes, the Firebase server sends an update to the client over the open socket. When the client receives such an update, it invokes the completion block again with the updated value (or lack thereof).
Firebase Observer Event Type
The .child*
events fire on child nodes of the location/query that you observe, while .value
fires on the location/query itself. This means that the value you get is one level higher up in the JSON, and you'll need to loop over the results:
mostRecent.observe(.value) { (snapshot: DataSnapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let (post, error) = PostFetcher.parsePostSnapshot(snapshot: child)
if let post = post {
self.latestPosts.append(post)
if let postId = post.postId { print("PostId = \(postId)") }
}
if let error = error {
print("\(#function) - \(error)")
}
}
}
Alternatively, you can listen to the .childChanged
and .childRemoved
events (in addition to .childAdded
that you already have) and handle them separately. This may be better in cases where you need to update the UI efficiently, since it allows you to handle each individual case (new node, changed, node, removed node) in the most efficient way.
Swift 4, Firebase how to observe the snapshot and convert into object
Use the below code to get your required values
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let ref = Database.database().reference().child("users").child("\(uid)")
ref.child("Dream").observe(.value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
print(dictionary["content"] as? String)
}, withCancel: nil)
ref.child("Grocerylist").observe(.value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
print(dictionary["content"] as? String)
}, withCancel: nil)
Completion handler Firebase observer in Swift
The code you're using declares stadiums
outside of the observer. This means any time a change is made to the value of the database reference, you're appending the data onto stadiums
without clearing what was there before. Make sure to remove the data from stadiums
before appending the snapshots again:
func getStadiums(complition: @escaping ([Stadium]) -> Void){
var stadiums: [Stadium] = []
let stadiumRef = Database.database().reference().child("Stadium")
stadiumRef.observe(.value, with: { (snapshot) in
stadiums.removeAll() // start with an empty array
for snap in snapshot.children {
guard let stadiumSnap = snap as? DataSnapshot else {
print("Something wrong with Firebase DataSnapshot")
complition(stadiums)
return
}
let stadium = Stadium(snap: stadiumSnap)
stadiums.append(stadium)
}
complition(stadiums)
})
}
Why does Firebase observeEventType return a snapshot everytime in viewWillAppear?
One answer is to simply let the observer do it's job and update your data in the background.
So if the user is on controllerA the observer updates a datasource (an array) so then the UI is updated from that array.
When the user switches to controllerB, the observer can still update the array, but don't update the UI in controllerA since there's no need to.
When the user switches back to A you'll have current data available so just reload the tableView (assuming iOS here) from the array.
This solution reduces the 'polling' nature and let's Firebase do the heavy lifting to notify your app when it needs to. You're just reloading the tableViews from an array when that controller becomes active.
Edit
The idea here is to add the observer once - perhaps when the view loads the first time only (viewDidLoad) or maybe in your app delegate. Once you add the observer it will update your dataSource arrays when data changes so when you move from view to view the only action needed will be to reload the tableView from the updated array.
There are times when you may want to remove an observer but it doesn't sound like you need to do that from your question. So - attach the observers once and let them update the dataSource arrays as the data changes. Reload your tableViews when switching views.
How do you convert a firebase snapshot of observeDataEventType.value to observeSingleEvent?
This function
someRef.observeSingleEvent(of: .value, with: { snapshot in
reads the node someRef
and returns its child data as a snapshot one time and one time only, not leaving an observer. Further changes will not fire an event (the code in the closure)
This
someRef.observe(.value, with: { snapshot in
Adds an observer to the someRef node, retrieving its child data and leaving an observer on that node. Any future changes of any kind will cause an event and the code in the closure is passed a snapshot containing all of the child data.
Note that both functions are expecting a DataEventType so you can just shorten it to .value, .childAdded etc.
So in essence you don't need to 'convert' one to the other as they have different functions but return the same data - at least initially. .observe will continue to feed data back to the app as it's child data changes, and that may or may not be what you want.
Swift - How to add a removed observer in firebase
Issue is resolved not by using addObserver method but by doing this.
- Created a snapshot class level of var for snapshot
var dataSnapshot: ((DataSnapshot)->Void)?
- Saved the snapshot
dataSnapshot = { (snapshot: DataSnapshot) in
....
}
- Set observer using data snapshot
firebaseRef.observe(DataEventType.value, with: dataSnapshot)
- Removed the observer
firebaseRef?.removeObserver(withHandle: refHandle)
When I needed to add the observer again I just started another observer with the same snapshot
Set observer using data snapshot
firebaseRef.observe(DataEventType.value, with: dataSnapshot)
Related Topics
Why Reloaddata Is Showing Error for My Tableview Inside Viewcontroller
How to Execute Different Implementation of a Method of a Generic Struct Based on the Generic Type
How to Know When to Call a Closure with () or Without Parentheses in Handler:
Making Gradient Background in Swift - the Right Way
How Assistant Model Works in Swift
Swift "Retry" Logic on Request
Getting Data Out of Completionhandler in Swift in Nsurlconnection
Uitextview Linktextattributes Font Attribute Not Applied to Nsattributedstring
How to Correctly Initialize an Unsafepointer in Swift
Using Reflection to Set Object Properties Without Using Setvalue Forkey
Differencebetween Convenience Init VS Init in Swift, Explicit Examples Better
Using the Swift If Let with Logical and Operator &&
What Does an Exclamation Mark in a Property in Swift Language
What Is the Shortest Way to Run Same Code N Times in Swift
How to Use a Switch Statement with a Nested Enum
Color Ouput with Swift Command Line Tool
Check If on Correct Dispatch Queue in Swift 3
Getting a String Value from Nsorderedset Using Swiftui Foreach