Blog Infos
Author
Published
Topics
, , , ,
Published

Coil is a powerful, fast, lightweight image-loading library that many Android developers have used for years. After the introduction of compose multiplatform, several open source image loading libraries have been introduced by the community such as Kamel and Compose Image Loader.
There was still an option of using coil using expect and actual but only on Android.

However, thanks to the Coil team, announced they will be working on supporting compose multiplatform. The good news is that Coil3 supports Android, IOS, Desktop and Wasm.

Let us learn step by step how to start using coil3 for the CMP project.

Setup:

Let’s start with dependencies

coil3 = "3.0.0-alpha06"
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil3" }
coil-compose-core = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil3" }
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor", version.ref = "coil3" }
coil-mp = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }

Coil 3 needs Ktor wasm to support image loading on the wasm platform.

ktor = "3.0.0-wasm2"
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }

You also need to add this repository to the settings.gradle plugin management block.


pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
        maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental")
        maven( "https://androidx.dev/storage/compose-compiler/repository")
        maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
        maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental")
        maven( "https://androidx.dev/storage/compose-compiler/repository")
        maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
    }
}

Now add these to your build.gradle in the common module

        implementation(libs.ktor.core)
        implementation(libs.coil.compose.core)
        implementation(libs.coil.compose)
        implementation(libs.coil.mp)
        implementation(libs.coil.network.ktor)

previously in Android, we used to do this

class MyApplication : Application(), ImageLoaderFactory {
    override fun newImageLoader(): ImageLoader {
        return ImageLoader.Builder(this)
            .crossfade(true)
            .build()
    }
}

in Compose multi-platform we have to build an image loader as well

let’s create the image loader

fun getAsyncImageLoader(context: PlatformContext)=
    ImageLoader.Builder(context).crossfade(true).logger(DebugLogger()).build()

Notice that similar to what we used to do in Android the builder still needs a context, but the context does not exist in KMP, We can get the platform context using a composition local

 LocalPlatformContext.current

but we don’t need it yet, since like context we don’t have an application class in kmp so there’s a simple function in coil which you need to call in the start of your top-level composable.

internal fun App() = AppTheme {


    setSingletonImageLoaderFactory { context ->
        getAsyncImageLoader(context)
    }
//your content here
}

The official doc describes it like this

and that’s all the setup. Let us start loading images now.

Just like Android, you can use AsyncImage and SubComposeAsyncImage.

Async Image:
 coil3.compose.AsyncImage(
        modifier = modifier,
        model =url,
        contentDescription = contentDescription,
        contentScale = contentScale,
    
        onError = {
           //update state 
        },
        onLoading = {
           //update state
        },
        onSuccess = {
           //update state
        },
  
    )
SubComposeAsyncImage:
 SubcomposeAsyncImage(
        modifier = modifier,
        model = url,
        loading = {
         Text("Loading")
        },
        contentScale = contentScale,
        contentDescription = contentDescription
    )

this is the same as Android let’s now move to caching and caching policies.

Memory Cache:

To use the memory cache you need to add some new parameters to the image loader that we created earlier, First enable the memory cache policy

    ImageLoader.Builder(context).memoryCachePolicy(CachePolicy.ENABLED)

now inside the memory cache lambda create a memory cache object, maxSizePercent defines how much space in memory should be used in memory to cache the images.

.memoryCache {
        MemoryCache.Builder().maxSizePercent(context, 0.3).strongReferencesEnabled(true).build()
    }

you can also enable /disable strong and weak references.

        /**
         * Enables/disables strong reference tracking of values added to this memory cache.
         */
        fun strongReferencesEnabled(enable: Boolean) = apply {
            this.strongReferencesEnabled = enable
        }

        /**
         * Enables/disables weak reference tracking of values added to this memory cache.
         * Weak references do not contribute to the current size of the memory cache.
         * This ensures that if a [Value] hasn't been garbage collected yet it will be
         * returned from the memory cache.
         */
        fun weakReferencesEnabled(enable: Boolean) = apply {
            this.weakReferencesEnabled = enable
        }

result, notice we are also passing a logger to check when the library is loading from memory and when from the cache.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

When sharing isn’t caring: Using platform-specific code in Kotlin Multiplatform

Sharing code across platforms is a wonderful superpower. But sometimes, sharing 100% of your codebase isn’t the goal. Maybe you’re migrating existing apps to multiplatform, maybe you have platform-specific libraries or APIs you want to…
Watch Video

When sharing isn’t caring: Using platform-specific code in Kotlin Multiplatform

Russell Wolf
Kotlin Multiplatform Developer

When sharing isn’t caring: Using platform-specific code in Kotlin Multiplatform

Russell Wolf
Kotlin Multiplatform ...

When sharing isn’t caring: Using platform-specific code in Kotlin Multiplatform

Russell Wolf
Kotlin Multiplatform Deve ...

Jobs

fun getAsyncImageLoader(context: PlatformContext) =
    ImageLoader.Builder(context).memoryCachePolicy(CachePolicy.ENABLED).memoryCache {
        MemoryCache.Builder().maxSizePercent(context, 0.3).strongReferencesEnabled(true).build()
    }.crossfade(true).logger(DebugLogger()).build()
Disk Cache:

Disk cache differs in all the different platforms, but luckily we have okio,
with coil and OKIO we can similarly use disk cache like memory cache. Before proceeding, we’ll need to make some changes to our getAsyncImageLoader method.

fun getAsyncImageLoader(context: PlatformContext) =
    ImageLoader.Builder(context).diskCachePolicy(CachePolicy.ENABLED).networkCachePolicy(CachePolicy.ENABLED).diskCache {
        newDiskCache()
    }.crossfade(true).logger(DebugLogger()).build()

fun newDiskCache(): DiskCache {
    return DiskCache.Builder().directory(FileSystem.SYSTEM_TEMPORARY_DIRECTORY / "image_cache")
        .maxSizeBytes(512L * 1024 * 1024) / 512MB
        .build()
}

We need a simple disk cache object to handle disk cache, where we can set the directory and maximum size in bytes. Coil truly stands out as a potent, user-friendly, and uncomplicated library for this purpose.

Now the image is first loaded from the network and the second time its loaded from the disk.

Let’s do some troubleshooting, when running the desktop app you might get this exception

Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' and ensure it has the same version as 'kotlinx-coroutines-core'

to resolve this, we need to use swing coroutines

kotlinxCoroutinesSwing = "1.8.0"
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutinesSwing" }

add it to jvmMain

jvmMain.dependencies {
            implementation(libs.ktor.java)
            runtimeOnly(libs.kotlinx.coroutines.swing)
        }

it should now load perfectly.

Happy Coding ❤

All the code is available in this repo

https://github.com/Kashif-E/Coil3-Compose-Multiplatform?source=post_page—–5745ea76356f——————————–

Connect with me on Linkedin:

https://www.linkedin.com/in/kashif-mehmood-km/

This is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE
Menu