Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Kotlin Coroutines support

Amplify Android v1 is now in Maintenance Mode until May 31st, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v1.

Please use the latest version (v2) of Amplify Library for Android to get started.

If you are currently using v1, follow these instructions to upgrade to v2.

Amplify libraries should be used for all new cloud connected applications. If you are currently using the AWS Mobile SDK for Android, you can access the documentation here.

Amplify provides an optional and separate API surface which is entirely focused on using Kotlin's coroutines and flows.

To use it, import Amplify facade from core-kotlin instead of from core. See the Installation notes below for more details.

With the Coroutines APIs, most Amplify functions are expressed as suspend functions. Suspending functions can be launched using one of the lifecycle-aware coroutine scopes in the Android Architecture components:

import com.amplifyframework.kotlin.core.Amplify
// ...
val post = Post.builder()
.title("My First Post")
.build()
lifecycleScope.launch {
try {
Amplify.DataStore.save(post) // This is suspending function!
Log.i("AmplifyKotlinDemo", "Saved a post")
} catch (failure: DataStoreException) {
Log.e("AmplifyKotlinDemo", "Save failed", failure)
}
}

Coroutines can greatly improve the readability of dependent, asynchronous calls. Moreover, you can use scopes, dispatchers, and other Kotlin coroutine primitives to get more control over your execution context.

Let's consider what happens when you have three dependent operations. You want to save a Post, then an Editor, and finally a PostEditor. With Amplify's coroutines interface, you can write these operations sequentially:

lifecycleScope.launch {
try {
listOf(post, editor, postEditor)
.forEach { Amplify.DataStore.save(it) }
Log.i("AmplifyKotlinDemo", "Post, Editor, and PostEditor saved")
} catch (failure: DataStoreException) {
Log.e("AmplifyKotlinDemo", "An item failed to save", failure)
}
}

In Amplify's vanilla APIs, this would have created a large block of code with three nested callbacks.

Install coroutine support

Amplify's coroutine support is included in an optional module, core-kotlin.

  1. Under Gradle Scripts, open build.gradle (Module :app), and add the following line in dependencies:

    dependencies {
    // Add the below line in `dependencies`
    implementation 'com.amplifyframework:core-kotlin:ANDROID_V1_KOTLIN_VERSION'
    }
  2. Wherever you use the Amplify facade, import com.amplifyframework.kotlin.core.Amplify instead of com.amplifyframework.core.Amplify:

    import com.amplifyframework.kotlin.core.Amplify

Using Kotlin primitives

Amplify tries to map the behavior of your callback-based APIs to Kotlin primitives in an intuitive way. Functions whose callbacks emit a single value (or error) are now expressed as suspending functions, returning the value instead. Functions whose callbacks emit a stream of values will now return Kotlin Flows, instead.

Special cases with Kotlin

Some APIs return an operation which can be cancelled. Examples include realtime subscriptions to an API, and uploading/downloading objects from Storage.

API subscriptions with Kotlin

The API category's subscribe() function uses both a suspend function and a Flow. The function suspends until the API subscription is established. Then, it starts emitting values over the Flow.

lifecycleScope.async {
try {
Amplify.API.subscribe(request) // Suspends until subscription established
.catch { Log.e("AmplifyKotlinDemo", "Error on subscription", it) }
.collect { Log.i("AmplifyKotlinDemo", "Data on subscription = $it") }
} catch (error: ApiException) {
Log.e("AmplifyKotlinDemo", "Failed to establish subscription", error)
}
}

Storage upload and download operations with Kotlin

The Storage category's downloadFile() and uploadFile() functions are bit more complex. These APIs allow you to observe transfer progress, and also to obtain a result. Progress results are delivered over a Flow, returned from the progress() function. Completion events are delivered by a suspending result() function.

// Download
val download = Amplify.Storage.downloadFile(remoteKey, localFile)
lifecycleScope.async {
download
.progress()
.collect { Log.i("AmplifyKotlinDemo", "Download progress = $it") }
}
lifecycleScope.async {
try {
val result = download.result()
Log.i("AmplifyKotlinDemo", "Download finished! ${result.file.path}")
} catch (failure: StorageException) {
Log.e("AmplifyKotlinDemo", "Download failed", failure)
}
}
// Upload
val upload = Amplify.Storage.uploadFile(remoteKey, localFile)
lifecycleScope.async {
upload
.progress()
.collect { Log.i("AmplifyKotlinDemo", "Upload progress = $it") }
}
lifecycleScope.async {
try {
val result = upload.result()
Log.i("AmplifyKotlinDemo", "Upload finished! ${result.key}")
} catch (failure: StorageException) {
Log.e("AmplifyKotlinDemo", "Upload failed", failure)
}
}