Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase: How to use observeSingleEventOfType for latest server data when persistance is enabled?

I really enjoy coding with Firebase. It's a great backend with a variety of different toolsets. But I miss a simple way to check a path for updated data when persistance is enabled. I think that this is not a seldom usecase, because I often need my app to act in a certain way depending on the latest server data, that only need to be read once.

I would usually use observeSingleEventOfType, but it's quite useless when perisistance is enabled, since it will never retrieve the latest server data. Which I don't understand why. There should be an option added to skip the local cache and only look for server data.

Disabling persistance solves this problem and observeSingleEventOfType will work as expected. But that would mean that one needs to reimplement all the offline capabilities on his own.

First scenario:

 // chats contain meta information about the chat like last message and count of unread messages

 let chatRef = ref.child("chats").child(receiverId).child(chatId)
 chatRef.observeSingleEventOfType(.Value, withBlock: { (snapshot) -> Void in
     if !snapshot.exists() {
         print("snapshot does not exist")
         // the other side has deleted the chat
         // now delete all messages and member objects

         ref.child("messages").child(chatId).setValue(nil)
         ref.child("members").child(chatId).setValue(nil)
     } else {
         print("snapshot exists")
     }
 })

I also tried chatRef.keepSynced(true) before observing for events with no luck. Which doesn't make sense in all situations anyway:

Second scenario:

func removeOlderMessages() {
    let dateInThePast = NSDate().addDays(-30).timeIntervalSince1970 * 1000
    self.messagesRef.queryOrderedByChild("timestamp")
        .queryEndingAtValue(dateInThePast)
        .observeSingleEventOfType(.Value, withBlock: { (snapshot) -> Void in
            snapshot.ref.removeValue()
    })
}

Using keepSynced here would lead to downloading all messages in messagesRef, which is not wanted at all.

So is there a clever workaround for these two scenarios? Any help is appreciated.

like image 544
MJQZ1347 Avatar asked Jun 25 '16 23:06

MJQZ1347


1 Answers

Ok, I think I found a reasonable workaround for both scenarios:

Workaround for first scenario:

Use transactions. They will only work when you are online. The completition block will return the latest server data.

self.ref.child("chats").child(receiverId).child(chatId).runTransactionBlock({ (currentData) -> FIRTransactionResult in
    // Actually do nothing with the retrieved data and re-submit it.
    return FIRTransactionResult.successWithValue(currentData)
 }) { (error, success, snapshot) in

    if let error = error {
        print(error)
        return
    } else if !success || snapshot == nil {
       return
    }

    // snapshot contains the latest server data
    if !snapshot!.exists() {
       // the other side has deleted the chat
       // now delete all messages and member objects

       print("snapshot doesn't exist. deleting messages and members.")
       ref.child("messages").child(chatId).setValue(nil)
       ref.child("members").child(chatId).setValue(nil)     
    } else {
       print("snapshot exists. not deleting all messages and members.")
    }     
}

The downside is that it will take considerably longer to retrieve the data compared to observeEventType or observeSingleEventOfType.

Workaround for second scenario:

Use observeEventType(.Value). It will first return the cached and then latest server data if available. The observer can be removed after a set time interval with NSTimer.

All in all these workarounds are ok for now, but a function to skip the local cache when using observeSingleEventOfType is indispensable.

like image 161
MJQZ1347 Avatar answered Oct 20 '22 01:10

MJQZ1347