Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Use Async/Await/Coroutines In OnCompleteListener Firebase

I am building a client application which uses Firebase for two things:

  • User Authentication
  • Using a realtime database

I have managed to set up everything correctly on my client and on my backend server (using Firebase's Admin SDK) and am able to correctly authenticate users and allow them to read/write to the database.

I am also using Retrofit2 to send requests from the client to the backend.

As part of allowing users access to the database, it is needed to send the user's token to the backend so the user can be verified.

To do this, I have the following logic:

val user = FirebaseAuth.getInstance().currentUser

    if (user != null) {
        user.getIdToken(false).addOnCompleteListener {
            if (it.isSuccessful) {
                val token = it.result?.token
                //retrofit logic to send request happens from here
            }
       }

As you can see, getting the Id token of the user is an asynchronous call and in the current code base that I have, I have this code block for each one of my calls to the backend (duplication).

I want to know how I can export this snippet to a function (maybe a suspend method?) so that it can be reused for every call to the backend

I have searched online and have seen many SO questions, but none that fit this scenario. I have thought about passing in a callback, but I have several methods that communicate to the backend, and each of them will require a different callback method.

The solution I am looking for looks something like this:

fun fetchDataFromDB() {
  getIdTokenForUser()
  //wait till it finishes and then
  //perform request to DB
}

fun updateDataInDB() {
  getIdTokenForUser()
  //wait till it finishes and then
  //perform request to DB
}

//......

I have tried reading about and implementing coroutines, but I lack the knowledge to do so correctly.

EDIT

Thanks to @Doug Stevenson for his answer and direction, I have managed to construct the following:

private suspend fun getUserIdToken(user: FirebaseUser) = coroutineScope {

    val job = async {
        user.getIdToken(false).result?.token
    }
    job.await()
}

And I use it in this fashion:

fun updateDB(context: Context) = runBlocking {

    val user = FirebaseAuth.getInstance().currentUser

    if (user != null) {
        val token = getUserIdToken(user)
    }

  }

Is this the correct approach? Since the answers given below present a different implementation.

like image 322
tomerpacific Avatar asked Dec 30 '22 20:12

tomerpacific


1 Answers

getIdToken is asynchronous returns a Task object. If you want to use a Task object in a Kotlin coroutine, you can use the library kotlinx-coroutines-play-services to add an extension method await() to the Task that makes it usable in a coroutine. With that, you can write something like this:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.9"
import kotlinx.coroutines.tasks.await

suspend fun getIdTokenForUser(user: FirebaseUser): GetTokenResult {
    return try {
        user.getIdToken(false).await()
    }
    catch (e: Exception) {
        // handle error
    }
}

You might have to update the types here - I didn't try to compile or test this.

See also:

  • Android kotlin task to be executed using coroutines
  • Coroutines And Firebase: How to Implement Javascript-like Promise.all()
  • Using Firebase with Kotlin coroutines
like image 87
Doug Stevenson Avatar answered Jan 04 '23 17:01

Doug Stevenson