32

My app uses Firebase to sync and restore data. I use the setValue:withCompletionBlock: method to insert, update and delete Firebase objects. This method is called whenever there is a CoreData save, thus syncing all my local changes to Firebase

- (void) setValue:(id)value withCompletionBlock:(void (^)(NSError* error, Firebase* ref))block;

Now syncing uploads all the local data to firebase, while restore replaces the local data with firebase data.

- (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block;

I observe FEventTypeValue and use the FDataSnapshot to get data from the Firebase and restore the local data.

So everything works perfectly for me until I set persistence to Firebase.

[Firebase setOption:@"persistence" to:@YES];

Now when persistence is on, when I update, say insert an object into Firebase, and then restore, the data before the insertion is restored. ie the newly inserted object is not restored. However if I restore again, the inserted object is restored. The same thing happens when an object is deleted. The deleted object reappears when I restore for the first time and vanishes when I restore again. I can see that the Firebase objects are inserted and/or deleted correctly through the Firebase data view.

I'm not sure what I'm doing wrong here. I only have issue when I restore. I think the Firebase cache is causing this restore issue. I'm thinking of clearing the Firebase cache before I restore. My question is

  1. Is clearing the cache before a restore a good method?
  2. If yes, how to clear the Firebase cache?
  3. If no, can you suggest me the best method to restore data.
Infinite Loops
  • 671
  • 3
  • 11
  • 23
Thahir
  • 901
  • 1
  • 8
  • 16

2 Answers2

26

[NOTE: If using Cloud Firestore instead of Realtime Database is an option for you, it has much better offline support. When you do a .get(), it will automatically attempt to fetch the latest data from the server and only use cached data if you are offline. You can also specifically request to retrieve data from either the server or cache.]

This is unfortunately expected behavior. You can probably work around it by using observeEventOfType instead of observeSingleEventOfType

Basically, whenever you observe data, we're going to pull data from our persistent cache first. That data will be whatever data we last received from Firebase. Because you're using observeSingleEventOfType instead of observeEventOfType, you're not going to be receiving regular updates from Firebase and so the data we have cached actually won't include the latest data that you wrote.

As a simple fix, you may be able to just add an observeEventOfType on the data in question. You don't actually need to do anything with the events. But if you listen to them all the time, then your app will be getting the latest data from firebase and when you call observeSingleEventOfType, you can be confident that it'll have the latest data cached.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Michael Lehenbauer
  • 16,229
  • 1
  • 57
  • 59
  • 9
    Is this still the case? It appears to be as I'm running into this very issue. Check if username exists, if no, create user. If the user signs out and tries to create a new user with the same username it will work because cache doesn't get updated. – Daniel Wood Apr 12 '16 at 14:15
  • 10
    I've been a huge fan of Firebase. But for this, I've to say honestly this is a big limitation. Firebase should add a new parameter in the method `fetchNewData: Bool`. The expected behavior that's unexpected is not expected behavior. There's a lot of case where this might fail, e.g. Pagination. Most of us wouldn't do observe .value for a page when doing pagination, otherwise the previous page might be refreshed, and pagination concept itself (that's to reduce data transfer) became useless since the observe method keeps refreshing the previous page data, and we also can't use `removeObserver` – Edward Anthony Oct 11 '16 at 22:40
  • Any fix for this? – Relm Oct 22 '16 at 09:04
  • 4
    I don't think so! It's still the case after more than 2 years! – kirtan403 Oct 26 '16 at 13:43
  • 8
    @michael-lehenbauer do you have any ETA for a fix? – Soto_iGhost Nov 01 '16 at 01:22
  • This issue is till going on in 2019..... I love Firebase but I wish they were faster at updating their platform. – Supertecnoboff Mar 15 '19 at 11:07
  • @Supertecnoboff Thanks for being a Firebase fan and sorry things haven't improved here! I've updated the answer to make it clear that the current behavior is unlikely to change at this point. We actually did a much better job with our offline APIs in Cloud Firestore, so if switching is an option for you, you may be happier. Else, at least keep it in mind for future projects. Best regards! – Michael Lehenbauer Mar 15 '19 at 16:15
  • Firebase Persistence - just useless feature. You'll never get updated data otherwise you alway have to NOT believe the first response from the request, but you also never know if it will more data later. So the workaround is so messy that it causes more problems then solutions. Just waste of time. – Yura Buyaroff Jun 06 '21 at 21:29
2

You can use the snap.metadata.fromCache flag to check if the value is coming from the cache or not. Note that if the cache value and the value from the server match it won't fire your onSnapshot twice unless you add the includeMetadataChanges flag seen below, instead it would only fire it once with the metadata.fromCache flag being set to true.

db.collection('users').doc(uid).onSnapshot({ includeMetadataChanges: true }, (snap) => {
   if (!snap.metadata.fromCache) {
        const user = snap.data();
        // Code here
    }
})
Ryan Taylor
  • 12,559
  • 2
  • 39
  • 34