rimes
rimes

Reputation: 921

Continue suspend functions from callback

I am using firestore as my backend database and saving my data looks like this:

suspend fun uploadDataToFirestore() {
  val firestore = Firebase.firestore
  var batch = firestore.batch
 
 -- fill batch with data --

  batch.commit().addOnCompleteListener {
    if (it.isSuccessful) {
      Timber.d("Successfully saved data")
     
      doAdditionalSuspendingStuff()
            
    } else {
      Timber.d("error at saving data: ${it.exception}")     
    }
}
 

The problem lies inside the onCompleteListener because I am not able to call additional suspending functions. Is there a way to call suspending functions from within the onCompleteListener but so that they are still attached to the same scope because I don't want the function uploadDataToFirestore to finish until doAdditionalSuspendingStuff() is executed.

Upvotes: 3

Views: 1197

Answers (1)

Alex Krupa
Alex Krupa

Reputation: 560

You can use kotlinx-coroutines-play-services artifact which contains a small set of utilities for converting between coroutines and Tasks API (which you'll also find common in other Play-related libraries).

You should be able to replace callback-based API (addOnCompleteListener()) with suspending Task.await() extension function:

suspend fun uploadDataToFirestore() {
    val firestore = Firebase.firestore
    val batch = firestore.batch

    try {
        batch.commit().await() // Suspend the coroutine while uploading data.

        Timber.d("Successfully saved data")
        doAdditionalSuspendingStuff()
    } catch (exception: Exception) {
        Timber.d("error at saving data: $exception")     
    }
}

await() also returns an unwrapped result (the T in Task<T>).

Under the hood it converts Task<T>.addCompleteListener() into a suspending function using suspendCancellableCoroutine. Source code is available here.

Upvotes: 6

Related Questions