15

Firebase anonymous sign in returns a task (which is basically Google promise implementation):

val task:Task<AuthResult> = FirebaseAuth.getInstance().signInAnonymously()

How it would be possible create a signInAnonymous wrapper where:

  • It is a suspend function, waiting for the task completion

    • suspend fun signInAnonymous(): Unit
  • It returns a Deferred object, delivering the result asynchronously

    • fun signInAnonymous() : Deferred
JP Ventura
  • 4,663
  • 5
  • 43
  • 62

4 Answers4

15

The package kotlinx.coroutines.tasks now includes the follwing utility functions:

public suspend fun <T> Task<T>.await(): T { ... }

From the docs:

Awaits for completion of the task without blocking a thread.
This suspending function is cancellable.
If the Job of the current coroutine is cancelled or completed while this suspending function is waiting, this function stops waiting for the completion stage and immediately resumes with CancellationException.

public fun <T> Task<T>.asDeferred(): Deferred<T> { ... }

From the docs:

Converts this task to an instance of Deferred.
If task is cancelled then resulting deferred will be cancelled as well.


So you can just do:

suspend fun signInAnonymouslyAwait(): AuthResult {
    return FirebaseAuth.getInstance().signInAnonymously().await()
}

or:

fun signInAnonymouslyDeferred(): Deferred<AuthResult> {
    return FirebaseAuth.getInstance().signInAnonymously().asDeferred()
}
David Miguel
  • 6,915
  • 3
  • 42
  • 48
  • 2
    For those looking for the maven repository, it's [kotlinx-coroutines-play-services](https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-play-services) – RickSanchez725 Oct 29 '20 at 19:26
  • Blog explanation here https://betterprogramming.pub/how-to-use-kotlin-coroutines-with-firebase-6f8577a3e00f – Rahul Kahale Mar 01 '21 at 19:43
11

Based on this GitHub library, here's a way to transform a Task into a suspending function in the "usual" way to adapt callback based async calls to coroutines:

suspend fun <T> Task<T>.await(): T = suspendCoroutine { continuation ->
    addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception ?: RuntimeException("Unknown task exception"))
        }
    }
}

You can also wrap it in a Deferred of course, CompletableDeferred comes in handy here:

fun <T> Task<T>.asDeferred(): Deferred<T> {
    val deferred = CompletableDeferred<T>()

    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            // optional, handle coroutine cancellation however you'd like here
        }
    }

    this.addOnSuccessListener { result -> deferred.complete(result) }
    this.addOnFailureListener { exception -> deferred.completeExceptionally(exception) }

    return deferred
}
wonsuc
  • 2,284
  • 17
  • 25
zsmb13
  • 69,803
  • 10
  • 174
  • 178
  • how to return the value from this function? @zsmb13 both of the function needs to be called inside async. e.g: ```val tokenFirebase: String = FirebaseInstanceId.getInstance().instanceId.asDeferred().await().token``` and ```val tokenFirebase: String = FirebaseInstanceId.getInstance().instanceId.await().token``` // await() show a warning, instead: suspend function 'await' should be called only from a coroutine or another suspend function – mochadwi Feb 05 '19 at 05:01
  • Yes, both the `await` on `Deferred` and the `await` presented in my answer needs to be called inside a coroutine, as they are suspending functions. I'm not really sure what you're asking though. If you're asking how to start a coroutine, you should see the [coroutine guide](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md). – zsmb13 Feb 05 '19 at 06:09
  • In my case, I want to get the token from firebase, but how to return value from async coroutines. I've tried this: ```val newToken: String by lazy { val token = with(FirebaseInstanceId.getInstance().instanceId) { /*GlobalScope.async { asDeferred().await() }*/ // for what? we await() this? if (asDeferred().isCompleted && result != null && isSuccessful) result?.token ?: "" else "" } token } ``` @zsmb13 but the blank or empty string still returned, instead, or did I use the wrong ways? thanks – mochadwi Feb 05 '19 at 06:32
3

To transform it into a coroutine-ready function, I would use the Tasks.await() function from the Tasks API:

suspend fun FirebaseAuth.signInAnonymouslyAwait(): AuthResult {
    return Tasks.await(this.signInAnonymously())
}

As for Deferred, i'd stick with zsmb13's answer

0

Add this to gradle

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.3'

And then you can use it like this:

suspend fun login(email: String, pass: String) {
    FirebaseAuth.getInstance().signInWithEmailAndPassword(email, pass).await()
}
Cyber Gh
  • 92
  • 2
  • 8