Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Offline issue with Firestore vs Firebase

I converted one of my apps to the new Firestore. I am doing things like saving a document on a button click, and then in the onSuccess listener, going to a different activity.

I also use the fact that Firestore save operations return tasks, to group tasks together using Tasks.whenAll:

val allTasks = Tasks.whenAll(
       createSupporter(supporter),,
       setStreetLookup(makeStreetKey(supporter.street_name)),
       updateCircleChartForUser(statusChange, createMode = true), 
       updateStatusCountForUser(statusChange))

      allTasks.addOnSuccessListener(this@SignUpActivity, successListener)
      allTasks.addOnFailureListener(this@SignUpActivity, onFailureListener)

Finally, I get the document id from a successful save and store it in preferences or in a local database for later use (within the onSuccessListener)

This all works great. Until there is a loss of network connectivity. Then everything falls apart, because the tasks never complete and the onSuccess/onFailure/onComplete listeners never get called. So the app just hangs.

I am working around this by checking for network availability before each save, and then doing a work-around by creating tasks without any listeners. I am also generating a document id locally using a UUID generator.

This, BTW, was not the way the app worked with the old firebase. In that case, everything ran nicely when offline and I saw documents getting synced up whenever the app came online.

My workaround for Firestore seems a terrible hack. Has anyone come up with a better solution?

See related Firestore database on insert/delete document callbacks not being invoked when there is no connection addOnCompleteListener not called offline with cloud firestore

like image 208
Dutch Masters Avatar asked Dec 16 '17 06:12

Dutch Masters


2 Answers

Cloud Firestore provide us feature for handle offline data but you need to use “Snapshot” (QuerySnapshot, DocumentSnapshot) to handle this case, unfortunately it not documented well. This is some code example (I use Kotlin Android) to handle case using Snapshot:

UPDATE DATA:

db.collection("members").document(id)
  .addSnapshotListener(object : EventListener<DocumentSnapshot> {
      override fun onEvent(snapshot: DocumentSnapshot?,
                           e: FirebaseFirestoreException?) {
          if (e != null) {
              Log.w(ContentValues.TAG, "Listen error", e)
              err_msg.text = e.message
              err_msg.visibility = View.VISIBLE;
              return
          }
          snapshot?.reference?.update(data)

      }
  })

ADD DATA:

db.collection("members").document()
 .addSnapshotListener(object : EventListener<DocumentSnapshot> {
     override fun onEvent(snapshot: DocumentSnapshot?,
                          e: FirebaseFirestoreException?) {
         if (e != null) {
             Log.w(ContentValues.TAG, "Listen error", e)
             err_msg.text = e.message
             err_msg.visibility = View.VISIBLE;
             return
         }
         snapshot?.reference?.set(data)

     }
 })

DELETE DATA:

db.collection("members").document(list_member[position].id)
   .addSnapshotListener(object : EventListener<DocumentSnapshot> {
       override fun onEvent(snapshot: DocumentSnapshot?,
                            e: FirebaseFirestoreException?) {
           if (e != null) {
               Log.w(ContentValues.TAG, "Listen error", e)
               return
           }
           snapshot?.reference?.delete()
       }
   })

You can see code example here: https://github.com/sabithuraira/KotlinFirestore and blog post http://blog.farifam.com/2017/11/28/android-kotlin-management-offline-firestore-data-automatically-sync-it/

like image 194
Sabit Huraira Avatar answered Oct 01 '22 12:10

Sabit Huraira


When there is a loss of network connectivity (there is no network connection on user device), neither onSuccess() nor onFailure() are triggered. This behavior makes sense, since the task is considered completed only when the data has been committed (or rejected) on the Firebase server. So onSuccess() will fire only when the task completes successfully.

There is no need to check for network availability before each save. There is a workaround that easily can help you see if the Firestore client indeed can't connect to the Firebase server, which is by enabling debug logging:

FirebaseFirestore.setLoggingEnabled(true);

Operations that write data to the Firestore database are defined to signal completion once they've actually committed to the backend. As a result, this is working as intended: while offline they won't signal completion.

Note that the Firestore clients internally guarantee that you can read your own writes even if you don't wait for the completion of the task from delete. The Firestore client is designed to continue functioning fine without an internet connection. So writing/deleting to the database without an internet connection is (by design) possible, and will never yield an error.

like image 26
Alex Mamo Avatar answered Oct 01 '22 12:10

Alex Mamo