Regarding Firebase documentation if you limit your query with "toLast" then when a child is added, if you already exceed the value (in my case f.e. 50) then child removed is also triggered because the first child is deleted from the scope.
The following listeners are the ones I have on the code:
firebaseReference.queryOrderedByKey().queryLimited(toLast: 50).observe(.childAdded...
firebaseReference.queryOrderedByKey().queryLimited(toLast: 50).observe(.childRemoved...
I would like to know if there is some way to differenciate when a child is really deleted or a child is just deleted from the scope.
Thank you so much,
any further information don't hestiate to ask.
Firebase does not send along information on why you're receiving the .childRemoved event, so there's no way to know it based on that.
The only thing I can quickly think of is adding a .value listener for each child, which will then fire with null when the child gets deleted.
You'll want to remove the .value listener after you receive the .childRemoved handler, to not have dangling listeners for each child you've ever seen, so this may become more work than it's worth.
Related
The official Firebase Documentation states:
ChildAdded
EventHandler< ChildChangedEventArgs > ChildAdded
Event raised when children nodes are added relative to this location.
Register a handler to observe when children are added relative to this
Query object. Each time time children nodes are added, your handler
will be called with an immutable snapshot of the data.
ChildRemoved
EventHandler< ChildChangedEventArgs > ChildRemoved
Event raised when children nodes are removed relative to this
location.
Register a handler to observe when children are removed relative to
this Query object. Each time time children nodes are removed, your
handler will be called with an immutable snapshot of the data.
ChildChanged
EventHandler< ChildChangedEventArgs > ChildChanged
Event raised when children nodes are changed relative to this
location.
Register a handler to observe changes to children relative to this
Query object. Each time time children nodes are changed, your handler
will be called with an immutable snapshot of the data.
So far I am using ChildRemoved and ChildAdded successfully, and they work solidly and with no hassle for what I am doing. In particular, these two listeners are independent, that is to say: the actions that make one of them fire are guaranteed not make the other one fire. So, I do not have to do any synchronization or scheduling to resolve conflicts.
My question is: Are these three listener types independent of each other?
That is to say:
For any given action, I am guaranteed that one, and at most one, of the three listeners will fire.
Only ChildAdded fires when the listener is first established. The other two listener types do not.
A note about the documentation, it would resolved ambiguity if the documentation for the ChildChanged would specify what it means by "childChanged": does it include addition/removal/moving/editing of a child, or does it only focus on editing to an existing child?
Each one of these events is a specific way to handle synchronization of data across clients.
.childChanged
will be called whenever something changes
So if you have a real time database like the following:
/store/<random-number-inserted-by-firebase-1>/name/store-1
/store/<random-number-inserted-by-firebase-1>/location/street-1
/store/<random-number-inserted-by-firebase-1>/items/0/name/name-1
/store/<random-number-inserted-by-firebase-1>/items/0/type/type-1
/store/<random-number-inserted-by-firebase-1>/items/1/name/name-2
/store/<random-number-inserted-by-firebase-1>/items/1/type/type-2
/store/<random-number-inserted-by-firebase-2>/name/store-2
/store/<random-number-inserted-by-firebase-2>/location/street-2
You start the childAdded, childChanged, and childRemoved observers on "/store" together.
If you change /store/<random-number-inserted-by-firebase-1>/items/0/type/type-1 to /store/<random-number-inserted-by-firebase-1>/items/0/type/new-type-1 your childChanged callback function will receive a dictionary ["random-number-inserted-by-firebase-1": ["name": "store-1", "location": "street-1", "items": [["name": "name-1", "type": "new-type-1"], ["name": "name-2", "type": "type-2"]] .childAdded listens for anything that changes in a node.
I'd like to update (or synchronize) the latest entry from a tree in my Firebase-Database.
The structure looks like this:
I want to observe the latest entry only, without fetching all entries as 'EventTypeChildAdded' does.
However, I really want to observe the latest entry, so I'd like to call a function whenever there is a new 'latest' entry, so when a child gets added.
I already found this piece of code.
(DatabaseRef).queryOrderedByKey().queryLimitedToLast(1).observeSingleEventOfType
But this does not seem to observe the latest entry.
I guess you are on the wrong way. childadded does exactly what you need. Check the doc again.
https://firebase.google.com/docs/database/admin/retrieve-data#child-added
For the first time childadded fetches all the list but then only the item just added to the list.
Child Added
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.
EDIT:
To limit the childadded query to the last item:
ref.queryLimited(toLast: 1).observe(.childAdded) { (snapshot) in
// parse snapshot to get the last item
The docs for the current Firebase Admin SDK as well as the 2.5.1 Firebase iOS SDK (now legacy) mentions some guarantees regarding events, the most relevant one to me being:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
Does this still stand with respect to the iOS SDK (the current docs for which have since removed the table containing the guarantees), especially when interlacing observe and observeSingleEvent calls? In other words, if I were to call this code on app startup:
ref.child("users").observe(.childAdded) { snapshot in
print("Child added")
}
ref.child("users").observeSingleEvent(of: .value) { snapshot in
print("Value event")
}
Will I get a guarantee that the childAdded events are fired before the value event? In other words, will I get something this in the console, assuming there are 3 children under users?
Child added
Child added
Child added
Value event
More context:
I'm trying to load an initial blob of data from Firebase, after which I want to inform the application that it has received all initial data. In other words, I want to do something like this answer has suggested. Some simple experiments affirm that the guarantee is maintained, but this answer also suggests that interlacing observe and observeSingleEvent calls when local data is available will break the guarantee.
I am running into a problem making sure data is synced with the database when using persistence. Before you run away I just have a question about a specific behavior, and I want to know if I am doing something wrong or if this is expected behavior.
Here is the basic setup I am using:
ref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
// Gets cached or live data
// Does all the things
ref.observe(.childAdded) { (snapshot: FIRDataSnapshot) in
// Gets new children added
// Adds the things
}
ref.observe(.childRemoved) { (snapshot: FIRDataSnapshot) in
// Gets removed children
// Removes the things
}
}
This works great in most use cases. When on the screen showing the data, in this case, a comments feed, The Data is added and removed perfectly.
More importantly, when coming back to a previously visited comment feed, the cache is loaded and then .childAdded is fired for every new comment that is not included in the cache. That's great.
The problem I am running into is when coming back to a previously visited comment feed where comments have been deleted while away. .childRemoved does not get invoked for each comment that has been deleted. Which is leaving stale data on the screen. I can see how this might be the expected behavior considering the data has been deleted and thus a snapshot is unavailable to be sent.
If that is the case, what would be the correct course of action to be sure a user is not presented a comment that has been deleted?
Let me know if any clarification is needed! Thank you.
I think what is happening here is a subtle race caused by the persistence behavior.
When you call observe, you set up a listener which will update as changes occur. When you call "observeSingleEvent", you generally do the same thing, but stop listening after the first result. However, with persistence enabled that first result will usually come from the cache. The call will, however, call a background update.
What I believe is happening here is:
Comment is deleted in comment feed while there are no listeners
The single event is attached, and the cached state including the deleted comment is returned
The comment feed is updated before the childRemoved listener is added
The childRemoved listener is added and does not fire
That said, I haven't repro'd this to validate for sure. One test though would be to setup the single event as a regular listener, and just cancel the listener one you receive one event. In connected states, that should prefer getting state from the backend, so you should see the comment feed with the comment deleted (the comment removed would never fire, but thats because the initial state would be coherent).
I have the following event handler in my datacontext:
manager.hasChangesChanged.subscribe(function (eventArgs) {
hasChanges(eventArgs.hasChanges);
});
and in Chrome I've set a break point on the "haschanges(eventArg.haschanges);" line.
The moment I load my app and the process of fetching data begins, this breakpoint is hit. It then proceeds to be repeatedly hit and the "hasChanges" property varies between "true" and "false" many times.
I know from further debug breakpoints that a simple query that "expands" a related table via its navigation property triggers a visit to my "hasChangesChanged" event handler.
What I don't know - as the "eventArgs" is so big and complex - is exactly which of my 5 or so related entities being retrieved is triggering the "true" on the "hasChanges" property. Is there a property within the eventArgs I can inspect to determine which current entity has caused the trip to the hasChangesChanged event handler?
I'm puzzled about why any of what I'm doing is setting "hasChanges" to true as all I do in the first instance is retrieve data. As far as I'm aware, nothing is changed whatsoever at the point the entity manager is convinced that something has changed.
To elaborate, my app prefetches lots of data used for a tree structure at the point where it is sitting waiting for first input from the user. As the user has not had an opportunity of touching anything in the app by this point, why would breeze think that any of the entities concerned have been changed when they've simply been read in from the database?
Use the EntityManager.entityChanged event if you want fine grained information about what has changed. This event gives much more detail but is fired much more often.
http://www.breezejs.com/sites/all/apidocs/classes/EntityManager.html