Firebase realtime snapshot listener using Coroutines

后端 未结 4 1716
轻奢々
轻奢々 2020-12-29 10:22

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

4条回答
  •  北荒
    北荒 (楼主)
    2020-12-29 11:05

    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.
       }
    }
    

提交回复
热议问题