Converting Callbacks to Kotlin Coroutines (with RevenueCat SDK Example)
Tags: android, kotlin, tutorial, mobile
Published: 2023-10-30 14:16:11

## Introduction

Modern Android codebases rely heavily on the new features of the Kotlin language. Coroutines have been a central part of Kotlin's ecosystem for quite some time now.

Many projects use Coroutines in their Services, Data Sources, and Repositories to leverage the power of writing asynchronous code and the readability of synchronous code.

If you have a modern Android codebase that relies on Courotines to keep your code simple and easier to maintain, you might encounter a third-party SDK that uses callbacks for their APIs. One of these is [RevenueCat Android SDK](https://www.revenuecat.com/docs/getting-started#4-using-revenuecats-purchases-sdk).

The problem? -&gt; Coroutines and callbacks do not mix together.

To keep your code nice and clean, you will need to transform the callbacks to Courutines.

## Coroutines & Callbacks

Kotlin gives us 2 ways to transform something into a Coroutine:

1. [`suspendableCoroutine`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/suspend-coroutine.html)
    
2. [`suspendCancellableCoroutine`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/suspend-cancellable-coroutine.html)
    

Both APIs serve the same purpose. The only difference is that `suspendCancellableCoroutine` supports the cancellation of the task at hand.

Most callbacks come with the following methods:

* a method for retrieving the result, eg. `onSuccess(actionResult: ActionResult)`
    
* a method for catching an error eg. `onError(error: Throwable)`
    
* (optional) a method for canceling
    

Implementation might look something like this:

```kotlin
SDK.performAction(object : ActionListener {
    override fun onSuccess(actionResult: ActionResult) {

    }

    override fun onError(error: Throwable) {

    }
})
```

## Wrapping Callbacks to Coroutines

Wrapping generally consists of 2 steps:

1. Create a `suspend` function which returns the object that you would get from the `onSuccess` callback method.
    
2. Use `suspendCoroutine` as the return block with the following:
	- `continuation.resume(result)` for success.
    - `continuation.resumeWithException(throwable)` for the error.
    

I'll use RevenueCat `getOfferings` as an example.

```kotlin
suspend fun Purchases.getOfferings(): Offerings {
    return suspendCoroutine { continuation ->
        getOfferings(object : ReceiveOfferingsCallback {
            override fun onError(error: PurchasesError) {
                continuation.resumeWithException(PurchasesException(error))
            }

            override fun onReceived(offerings: Offerings) {
                continuation.resume(offerings)
            }
        })
    }
}
```

`ReceiveOfferingsCallback` returns `Offerings` object, so our method should return the same. It also returns a `PurchasesError` object in case an error happens. Since this is not `Throwable` we need to create one.

```kotlin
open class PurchasesException(
    private val purchasesError: PurchasesError
) : Exception() {
    val code: PurchasesErrorCode
        get() = purchasesError.code

    override val message: String
        get() = purchasesError.message

    val underlyingErrorMessage: String?
        get() = purchasesError.underlyingErrorMessage

    override fun toString() = purchasesError.toString()
}
```

## Conclusion

Congratulations, you have successfully wrapped your first callback into a Coroutine 🎉.

Now you can use it like this:

```kotlin
try {
  val offerings = Purchases.getSharedInstance.getOfferings()
} catch (e: Exception) {
  Log.e("getOfferings#Error", e.message.orEmpty())
}
```

If you have enjoyed or found this helpful, make sure to follow me for more content like this!

Reposted from my [blog](https://dinkomarinac.me/converting-callbacks-to-kotlin-coroutines-with-revenuecat-sdk-example).

### Bonus

If you want to use Coroutines with the RevenueCat SDK, I've prepared a wrapper for you. Check out [RevenueKt](https://github.com/dinko7/RevenueKt).