Home » Android » android – Firestore runTransaction() and offline work

android – Firestore runTransaction() and offline work

Posted by: admin June 15, 2020 Leave a comment

Questions:

I’m implementing post liking and commenting functionality in Firestore using transactions. I use transaction because I need to add new field in likes/comments subcollection and update counter on the post, and also add post id to the user liked/commented post collection.

I noticed that if I’m offline and I request my post like this everything is OK:

val postDocRef = FirebaseUtil.postsColRef.document(postId)

postDocRef.get().addOnSuccessListener { doc ->
    val post = doc.toObject(Post::class.java)
    Timber.e(post.toString())
}

But if I do the same in transaction exception is thrown:

val postDocRef = FirebaseUtil.postsColRef.document(postId)

FirebaseUtil.firestore.runTransaction(Transaction.Function<Void> { transaction ->
    val post = transaction.get(postDocRef).toObject(Post::class.java)
}

Exception is:

com.google.firebase.firestore.FirebaseFirestoreException: UNAVAILABLE

Why offline mode is not working in transaction? Is it possible to implement this functionality (adding entry in subcollection and updating fields in different objects) in offline?

What could be drawbacks in replacing transaction with continueWithTask() call chain?

How to&Answers:

No, this isn’t possible with transactions because they are inherently network dependent. When you use a transaction, you are telling Firestore that you can only perform database operations synchronously, one client after the other. Transactions would be useful for stuff like in-game currency transfers where you need to ensure you don’t accidentally double up your writes and give a user too much or too little money.

If your like counter needs to have perfect precision, I would suggest using a subcollection where each document contains a reference to the user who liked a given post. Then, in a Cloud Function you can use a transaction to count the number of users who’ve like a post and ensure there are no miscounts. This has the added benefit of letting you know who liked a post which should be future proof if you decided to add more like related features. On the client side, you can “trick” it by writing to the counter even though you don’t have permission to do so. I haven’t tested this, but I’m pretty sure the write will succeed locally and then fail only once you’re back online. That doesn’t matter though because the Cloud Function will then sync the counter server side.

On the other hand, if you don’t really care about having super precise like counts, what you’re looking for is the WriteBatch class. This one is new in Firestore and really cool. I’m in the process of getting a post on Firestore I wrote published, but here’s an excerpt:

Cloud Firestore also includes an awesome new way to batch writes with
the WriteBatch class. It’s very similar to the
SharedPreferences.Editor you’ll find on Android. You can add or update
documents in the WriteBatch instance, but they won’t be visible to
your app until you call WriteBatch#commit(). I’ve created the standard
Kotlin improvement where the batch lifecycle is managed for you — feel
free to copypasta.

inline fun firestoreBatch(transaction: WriteBatch.() -> Unit): Task<Void> = FirebaseFirestore.getInstance().batch().run {
    transaction()
    commit()
}