Colton Idle
11/19/2021, 8:00 AMsuspend fun getThingFlow(myId: String): Flow<Thing?> {
val thingFlow = MutableStateFlow<Thing?>(null)
FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
thingFlow.emit(value!!.toObject<Thing>())
}
return thingFlow
}Colton Idle
11/19/2021, 8:06 AMsuspendCoroutine {
and
callbackFlow
are popular. Just overall confused on which would be the best approach here.KamilH
11/19/2021, 8:07 AMsuspend fun getThingFlow(myId: String): Flow<Thing?> =
suspendCancellableCoroutine<Thing> { continuation ->
FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
continuation.resume(value!!.toObject<Thing>()) // note that this `resume` method is extension function one, so you need to import it
}
it.invokeOnCancellation {
// do the cleanup here
}
}KamilH
11/19/2021, 8:08 AMFirebaseFirestore API, so be careful copy-pasting it, but generally it should be something like aboveColton Idle
11/19/2021, 8:12 AMColton Idle
11/19/2021, 8:15 AMKamilH
11/19/2021, 8:15 AMsuspend fun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
invokeOnClose {
// do the cleanup here
}
}KamilH
11/19/2021, 8:17 AMFirebaseFirestore API, so if there is any indication that this stream is finished (closed) you can use close() function to close this flowColton Idle
11/19/2021, 8:18 AMColton Idle
11/19/2021, 8:21 AMTgo1014
11/19/2021, 8:59 AMcallbackFlow is the way to go, but honestly I’m impressed there’s no flow api for Firebase SDK yethfhbd
11/19/2021, 10:49 AMJoffrey
11/19/2021, 10:56 AMcallbackFlow approach, the function shouldn't be suspending. Also, the listener should be assigned to a variable so it can be unregistered in awaitClose. (see the docs for callbackFlow, they are pretty helpful)Colton Idle
11/19/2021, 7:21 PMfun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
val listener = FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
invokeOnClose {
listener.close()
}
}
Thanks everyone!hfhbd
11/20/2021, 12:27 AMfun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
val listener = FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
invokeOnClose {
close()
}
awaitClose {
listener.close()
}Colton Idle
11/21/2021, 3:52 AMJoffrey
11/21/2021, 11:02 AMinvokeOnClose should be called at all here. Only awaitClosehfhbd
11/21/2021, 11:13 AMinvokeOnClose function should be part of listener to finish/close the flow when all items are emitted. Otherwise, this flow will never finish by itself.Joffrey
11/21/2021, 11:14 AMinvokeOnClose shouldn't be called in the listener either, only close() should (when you want to close the channel of the flow). invokeOnClose is a callback on the SendChannel itself, why doesn't really have its place in a callbackFlow setup AFAIUhfhbd
11/21/2021, 11:16 AMfun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
val listener = FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
listener.onError { // I dont know the FireStore API
close(it)
}
listener.invokeOnClose { // or onFinished. I dont know the FireStore API...
close()
}
awaitClose {
listener.close()
}Joffrey
11/21/2021, 11:26 AMinvokeOnClose was that of the SendChannel, and is an existing function. I didn't get that you were talking about a hypothetical closing callback of the listener. There is no such thing AFAIK in this case, it's just a subscription to receive updates. It has no end in itself, so the only way to end the flow is by cancelling it from the collector side, which awaitClose is meant to handle:
fun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
val listener = FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
awaitClose {
listener.close()
}
}hfhbd
11/21/2021, 11:31 AMColton Idle
11/22/2021, 6:33 PMJoffrey
11/22/2021, 6:33 PMColton Idle
11/22/2021, 6:34 PMJoffrey
11/22/2021, 6:35 PMawaitClose anywhere, your flow will terminate immediately. invokeOnClose just registers a handler but doesn't waitJoffrey
11/22/2021, 6:35 PMColton Idle
11/22/2021, 6:36 PMJoffrey
11/22/2021, 8:21 PMColton Idle
11/22/2021, 9:28 PMfun getThingFlow(myId: String): Flow<Thing?> =
callbackFlow<Thing?> {
val listener = FirebaseFirestore.getInstance()
.document("things/$myId")
.addSnapshotListener { value, error ->
trySend(value!!.toObject<Thing>())
}
awaitClose {
listener.remove()
}
}
The only real change I made was listener.remove() since there is no close() api (i dont know where I got that from)Colton Idle
08/13/2022, 1:36 PM