I want to be able to listen to realtime updates in Firebase DB\'s using Kotlin coroutines in my ViewModel.
The problem is that whenever a new message is created in t
I have these extension functions, so I can simply get back results from the query as a Flow.
Flow is a Kotlin coroutine construct perfect for this purposes. https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/
@ExperimentalCoroutinesApi
fun CollectionReference.getQuerySnapshotFlow(): Flow {
return callbackFlow {
val listenerRegistration =
addSnapshotListener { querySnapshot, firebaseFirestoreException ->
if (firebaseFirestoreException != null) {
cancel(
message = "error fetching collection data at path - $path",
cause = firebaseFirestoreException
)
return@addSnapshotListener
}
offer(querySnapshot)
}
awaitClose {
Timber.d("cancelling the listener on collection at path - $path")
listenerRegistration.remove()
}
}
}
@ExperimentalCoroutinesApi
fun CollectionReference.getDataFlow(mapper: (QuerySnapshot?) -> T): Flow {
return getQuerySnapshotFlow()
.map {
return@map mapper(it)
}
}
The following is an example of how to use the above functions.
@ExperimentalCoroutinesApi
fun getShoppingListItemsFlow(): Flow> {
return FirebaseFirestore.getInstance()
.collection("$COLLECTION_SHOPPING_LIST")
.getDataFlow { querySnapshot ->
querySnapshot?.documents?.map {
getShoppingListItemFromSnapshot(it)
} ?: listOf()
}
}
// Parses the document snapshot to the desired object
fun getShoppingListItemFromSnapshot(documentSnapshot: DocumentSnapshot) : ShoppingListItem {
return documentSnapshot.toObject(ShoppingListItem::class.java)!!
}
And in your ViewModel class, (or your Fragment) make sure you call this from the right scope, so the listener gets removed appropriately when the user moves away from the screen.
viewModelScope.launch {
getShoppingListItemsFlow().collect{
// Show on the view.
}
}