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?
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 theSharedPreferences.Editor
you’ll find on Android. You can add or update documents in theWriteBatch
instance, but they won’t be visible to your app until you callWriteBatch#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() }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With