Hello. I’m not sure if I can pass my data from an overriden function and can use Flow to pass the data from Repository to ViewModel. Here in example with Firebase I have a Suspension functions can be called only within coroutine body error. Maybe I could rewrite it somehow?
Repository
fun fetchFirebaseFlow(): Flow<List<MyData>?> = flow {
var ret: List<MyData>? = null
firebaseDb.child("data").addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data = dataSnapshot.getValue<List<MyData>>()
emit(data) // Error. How to return the data here?
}
override fun onCancelled(databaseError: DatabaseError) {
emit(databaseError) // Error. How to return the data?
}
})
// emit(ret) // Useless here
}
ViewModel
private suspend fun fetchFirebase() {
repo.fetchFirebaseFlow().collect { data ->
if (!data.isNullOrEmpty()) {
// Add data to something
} else {
// Something else
}
}
Yes, I tried it as it was recommended but my fetchFirebaseFlow became suspended and proceed only once in a loop. Also I tried MutableStateFlow but it returns data twice (first one with initialization) and also my function became suspended.
It’s not really clear what you’re issue is. Coroutines suspend, that’s kind of the point.
Is the issue that you have code after fetchFirebase that is not running? If so then you can launch a coroutine and call fetchFirebase inside. The new coroutine will suspend but not the code that launched it.
Flow.collect is like Sequence.forEach, it starts the “lazy” collection and doesn’t stop until the collection says it is done.
So a Flow that never finishes like the one you’ve implemented, will cause collect to never return.
awaitClose doesn’t return until the Flow is canceled by the collector or closed by the Flow implementation (otherwise it would finish immediately without any data). Neither happens in your code above.
You could fix your code by calling close after offer.
Honestly, Flow is not the best tool for returning a single async value. That’s like passing around a List<Int> when all you have is an Int. Plain old suspend methods are best for single results. callbackFlow is perfect for wrapping addValueEventListener, but for wrapping addListenerForSingleValueEvent, I’d suggest using suspendCancellableCoroutine. Then fetch would become just a suspend method.
P.S. actually, I use the loop to fetch different data from Firebase, so, could be it a good idea to use flow in this case if I move the loop within fetch()?
You are probably best served by writing a general helper (something like suspend FirebaseReference.fetch(): DataSnapshot) and the using that however you need.
Flow works best if you are getting data over time and acting on that data as it comes in (as opposed to accumulating it into a data class or collection before processing) . I can’t really tell if that’s your scenario or not.