Josh
Josh

Reputation: 2587

In Firebase iOS SDK, .childAdded is triggered every time a child is deleted. How can I stop this?

I am storing some data under a node called "notifications" within my Firebase database. When I add an event observer to "notifications" and set the event type to ".childAdded" the completion handler is called every time a child is added to the "notifications" node AND every time a child is deleted from the "notifications" node. First of all, I don't understand why this is happening as nothing is being added, only deleted. Is there some way to avoid this?

If I can't avoid the .childAdded block executing every time a child is deleted, is there someway to detect that it was actually a child deleted event and not a child added event? I would like to do an early return from the function if the event was child deleted. Below is my code for reference:

//Event observer for notifications 
notificationsReference.queryLimited(toLast: 1).observe(.childAdded, with: {
        (snapshot) in

        //Do some stuff here only on .childAdded 
        //If the event was child deleted, do nothing 
})

Upvotes: 4

Views: 1593

Answers (2)

Dracula
Dracula

Reputation: 3090

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"]
},

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

Reputation: 599726

A Firebase Query is like a view onto the data. In your case that view will always show the last item in the underlying collection.

Say that this is your collection:

item1
item2

With this data, your query will initially fire child_added for item2.

Now say you add an item:

item1
item2
item3

Since item3 is now the last item in the collection, the query will fire child_added for item3.

Now you remove item3:

item1
item2

Since item2 is now once again the last item in the query, it will fire child_added for item2.

Upvotes: 9

Related Questions