Interestingly I found the right approach to handle this from an alternate answer written by Frank for the JS Firebase SDK: child_added gets triggered after remove. I used an approach that modified his answer a bit from comments to his answer. Specifically the answer by Kato to how to discard initial data in a Firebase DB.
Essentially, in both answers, you need to have a timestamp (or some other numerically increasing value) in your child object that will help you sort your child objects. Then when you form the firebase query, you will set a condition that reads values where this property exceeds a minimum increasing value. This way, if values are deleted, they won't affect since their sort key will be less than the passed key.
The corresponding Swift code looks like this:
let databaseReference = Database.database().reference().child("messages")
let timeStampKeyInChildObject = "messageTimestamp"
databaseReference
.queryOrdered(byChild: timeStampKeyInChildObject)
.queryStarting(atValue: NSDate().timeIntervalSince1970)
.observe(.childAdded)
{ (snapshot) in
...
}
In my case, the child objects are chat messages that have a field called messageTimestamp
. When I store messages in firebase, the timestamp is set using NSDate().timeIntervalSince1970
. Hence I pass this value to queryStarting(atValue:)
.
Everytime the database is updated (in this case child delete/add/modify), this query will only be fired if the deleted/added/modified child had a timestamp greater than or equal to NSDate().timeIntervalSince1970
(whose value keeps increasing). This will happen only for the child that was currently added. Anything older (especially which was deleted) will have a smaller timestamp and won't be considered by our query. Hence solving the issue of delete causing the .childAdded
trigger to run.
I chose to use NSDate().timeIntervalSince1970
instead of the JS equivalent of Firebase.ServerValue.TIMESTAMP
which I think is, Firebase.ServerValue.timestamp()
, since it returns [AnyHashable : Any]
. And I don't know how to decode it.
Additionally, for performance add this to your Firebase RealtimeDB Rules. This will do the sorting on the database side and not on the client side (after downloading the data).
"messages": {
".indexOn": ["messageTimestamp"]
},