Franco
04/12/2020, 9:05 AMsuspendCancellableCoroutine
completes so I can unsubscribe from the callback that might return more values later on.
My use case is that I'm trying to get the user from Firebase using the onAuthStateChanged()
listener as they suggest here. However, I don't want to keep listening for this after getting it at the start of the app and if I don't unsubscribe from it then I get the error IllegalStateException: Already resumed, but proposed with update
.
This is an example of my code:
suspend fun getUser(): User = suspendCancellableCoroutine { continuation ->
val unsubscribe = firebaseAuth.onAuthStateChanged(
nextOrObserver = { user -> continuation.resume(user) },
error = { error -> continuation.resumeWithException(...) }
)
}
Erik
04/12/2020, 9:59 AMonAuthStateChanged
function returns an unsubscribe function. When you get the first callback, invoke the unsubscribe function before passing the user to resume the continuation.Franco
04/12/2020, 10:04 AMunsubscribe
function can't be found inside the callback itself.
Do you have any idea if I might be doing something wrong? The code is the same I shared plus adding the unsubscribe()
call inside the callback and the error I see is Unresolved reference: unsubscribe
Erik
04/12/2020, 10:06 AMval unsubscribe
in your example. Let me think of somethingFranco
04/12/2020, 10:08 AMErik
04/12/2020, 10:13 AMsuspendCancellableCoroutine { continuation ->
lateinit var unsubscribe: firebase.Unsubscribe /* or whatever type it is */
val onUser = { user: User ->
unsubscribe()
continuation.resume(user)
}
unsubscribe = firebaseAuth.onAuthStateChanged(
nextOrObserver = onUser,
error = { error -> continuation.resumeWithException(...) }
)
}
lateinit var
you could also use a nullable var
, which also isn't pretty 😛continuation.invokeOnCancellation { }
Franco
04/12/2020, 10:51 AMonComplete()
on the continuation 😕
I had tried something similar which had not worked, I guess the trick is to take the callback outside of the onAuthStateChanged
call.fatih
04/12/2020, 10:54 AMFranco
04/12/2020, 11:07 AMcompleted
from the onAuthStateChanged
@fatih, the one I'd like to have is the one from the coroutine continuationonAuthStateChanged
but it doesn't seem to be called after a value is received, I tested it just nowErik
04/12/2020, 11:17 AMresume
on it, i.e. when the suspend fun returns a value (or throws)Franco
04/12/2020, 12:07 PMonComplete
callback so I can call the unsubscribe
from without having to add the hacky codeDominaezzz
04/12/2020, 1:11 PMsuspend fun getUser(): User {
return callbackFlow<User> {
val unsubscribe = firebaseAuth.onAuthStateChanged(
nextOrObserver = { user -> channel.offer(user) },
error = { error -> channel.close(error) }
)
awaitClose { unsubscribe() }
}.first()
}
Franco
04/12/2020, 2:17 PMDominaezzz
04/12/2020, 2:24 PMsuspend fun getUser(): User {
val latch = CompletableDeferred<User>()
val unsubscribe = firebaseAuth.onAuthStateChanged(
nextOrObserver = { user -> latch.complete(user) },
error = { error -> latch.completeExceptionally(error) }
)
try {
return latch.await()
} finally {
unsubscribe()
}
}
Erik
04/12/2020, 3:00 PMonAuthStateChanged
and unsubscribe
must be decoupled through some communication mechanism, e.g. a channel or the completable deferred. I agree that a flow better represents this Firebase API.Franco
04/12/2020, 5:05 PM