FIRDataEventTypeValue and FIRDataEventTypeChildAdded firing order - ios

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.

Related

How to optimize performance of Results change listeners in Realm (Swift) with a deep hierarchy?

We're using Realm (Swift binding currently in version 3.12.0) from the earliest days in our project. In some early versions before 1.0 Realm provided change listeners for Results without actually giving changeSets.
We used this a lot in order to find out if a specific Results list changed.
Later the guys at Realm exchanged this API with changeSet providing methods. We had to switch and are now mistreating this API just in order to find out if anything in a specific List changed (inserts, deletions, modifications).
Together with RxSwift we wrote our own implementation of Results change listening which looks like this:
public var observable: Observable<Base> {
return Observable.create { observer in
let token = self.base.observe { changes in
if case .update = changes {
observer.onNext(self.base)
}
}
observer.onNext(self.base)
return Disposables.create(with: {
observer.onCompleted()
token.invalidate()
})
}
}
When we now want to have consecutive updates on a list we subscribe like so:
someRealm.objects(SomeObject.self).filter(<some filter>).rx.observable
.subscribe(<subscription code that gets called on every update>)
//dispose code missing
We wrote the extension on RealmCollection so that we can subscribe to List type as well.
The concept is equal to RxRealm's approach.
So now in our App we have a lot of filtered lists/results that we are subscribing to.
When data gets more and more we notice significant performance losses when it comes to seeing a change visually after writing something into the DB.
For example:
Let's say we have a Car Realm Object class with some properties and some 1-to-n and some 1-to-1 relationships. One of the properties is a Bool, namely isDriving.
Now we have a lot of cars stored in the DB and bunch of change listeners with different filters listing to changes of the cars collection (collection observers listening for changeSets in order to find out if the list was changed).
If I take one car of some list and set the property of isDriving from false to true (important: we do writes in the background) ideally the change listener fires fast and I have the nearly immediate correct response to my write on the main thread.
Added with edit on 2019-06-19:
Let's make the scenario still a little more real:
Let's change something down the hierarchy, let's say the tires manufacturer's name. Let's say a Car has a List<Tire>, a Tire has a Manufacturer and a Manufacturer has aname.
Now we're still listing toResultscollection changes with some more or less complex filters applied.
Then we're changing the name of aManufacturer` which is connected to one of the tires which are connected to one of the cars which is in that filtered list.
Can this still be fast?
Obviously when the length of results/lists where change listeners are attached to gets longer Realm's internal change listener takes longer to calculate the differences and fires later.
So after a write we see the changes - in worst case - much later.
In our case this is not acceptable. So we are thinking through different scenarios.
One scenario would be to not use .observe on lists/results anymore and switch to Realm.observe which fires every time anything did change in the realm, which is not ideal, but it is fast because the change calculation process is skipped.
My question is: What can I do to solve this whole dilemma and make our app fast again?
The crucial thing is the threading stuff. We're always writing in the background due to our design. So the writes itself should be very fast, but then that stuff needs to synchronize to the other threads where Realms are open.
In my understanding that happens after the change detection for all Results has run through, is that right?
So when I read on another thread, the data is only fresh after the thread sync, which happens after all notifications were sent out. But I am not sure currently if the sync happens before, that would be more awesome, did not test it by now.

Firebase doesn't call back when using '.childAdded'

I have the following code, written in Swift which I expect to be called each time a new record has been added to the my Database:
var databaseRef = Firebase()
databaseRef = Firebase.init(url: "<MY_PROJECT_URL>")
databaseRef.child(byAppendingPath: "channels").queryLimited(toFirst: 100).observe(.childAdded , with: { (snapshot) in
print("New Message input by user")
})
And this is my data structure:
So I basically create a listener for the branch 'channels'. The completion handler gets called only at the start of my program, then, never again. The strange thing is that if I use '.value' rather than '.childAdded' it does work! I am not interested in using '.value' since that returns me the whole chunk of data inside the 'channels' branch and I am really interested only in the single record that was added. (actually one of those L1... guys. A new one of course) Any ideas?
If you are observing messages from all users at the same time I'd recommend restructuring your Firebase data a bit. Make messages a top-level object, and then you can just observe that whole section. I don't know what your messages look like, but I assume if you do this you will need to add a reference to the user inside the message so that you can get the new message added and still see which user wrote it.

Firebase observeEventType .childAdded doesn't work as promised

When I call this observe function from in my viewcontroller, the .childadded immediately returns a object that was already stored instead of has just bin added like .childadded would suspect.
func observe(callback: RiderVC){
let ref = DBProvider.Instance.dbRef.child("rideRequests")
ref.observe(DataEventType.childAdded) { (snapshot: DataSnapshot) in
if let data = snapshot.value as? NSDictionary {
let drive = cabRide(ritID: ritID, bestemming: bestemming,
vanafLocatie: vanaf, taxiID: taxiID, status: status)
print(drive)
callback.alertForARide(title: "Wilt u deze rit krijgen?", message: "Van: \(vanaf), Naar: \(bestemming)", ritID: ritID)
}
}
}
When I try this function with .childchanged, I only get a alert when it is changed like it suppose to do, but when doing .chiladded, it just gets all the requests out of the database and those requests were already there.
When I add a new request, it also gives an alert. So it works, but how can I get rid of the not added and already there requests?
Does anybody know this flaw?
This is working exactly as promised. From the documentation:
Retrieve lists of items or listen for additions to a list of items.
This event is triggered once for each existing child and then again
every time a new child is added to the specified path. The listener is
passed a snapshot containing the new child's data.
That might seem weird at first, but this is generally what most developers want, as it's basically a way of asking for all data from a particular branch in the database, even if new items get added to it in the future.
If you want it to work the way you're describing, where you're only getting new items in the database after your app has started up, you'll need to do a little bit of work yourself. First, you'll want to add timestamps to the objects you're adding to the database. Then you'll want to do some kind of call where you're asking to query your database by those timestamps. It'll probably look something like this:
myDatabaseRef.queryOrdered(byChild: "myTimestamp").queryStarting(atValue: <currentTimestamp>)
Good luck!

With Firebase persistence, .childRemoved is not called when a child is removed*

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).

Can someone clearly explain difference between .Value, .ChildAdded, .ChildChanged, .ChildRemoved for FIRDataEventType?

I'm having trouble putting it into words. Can someone explain what the difference between the different FIRDataEventTypes and examples of when it would be used?
Example (SWIFT):
let queryRef = FIRDatabase.database().reference().child("user")
queryRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
or
queryRef.observeEventType(.Value, withBlock: { (snapshot) -> Void in
From testing, .Value returns one object while .ChildAdded returns multiple; When doing advanced queries .ChildAdded doesn't work but .Value somewhat works (deeper children are null).
tl;dr - Watch this video. It uses the old SDK in Android, but the idea is the exact same even for iOS.
Each one of these events is a specific way to handle synchronization of data across clients.
The Value event will fire each time any piece of data is updated. This could be a newly added key, a deletion of a key, or an update of any value at the reference. When the change happens the SDK sends back the entire state of the object, not the delta just change that occurred.
The Child added event will fire off once per existing piece of data, the snapshot value will be an individual record rather than the entire list like you would get with the value event. As more items come in, this event will fire off with each item.
The Child removed and changed events work almost the same. When an item is deleted or has it's value changed, the individual item is returned in the callback.

Resources