coil icon indicating copy to clipboard operation
coil copied to clipboard

Compose Multiplatform support

Open tiwiz opened this issue 4 years ago • 112 comments

Is your feature request related to a problem? Please describe. As an Android Developer, I would love to use Coil with Compose Desktop in the same way I use Coil for Compose on Android.

Describe the solution you'd like Ideally, it would be the best to have the same approach we have on Android, so that the knowledge can be reused.

Additional context I think it would be OK to link the image download scope to either a LaunchedEffect in a Composable, or just link it to the Application lifecycle.

tiwiz avatar Aug 03 '21 09:08 tiwiz

~I agree this would be cool to have. That said, I think this is very very unlikely to be implemented as Coil heavily relies on a number of Android framework classes (Bitmap, BitmapFactory, ImageDecoder) and integrates closely with Views and Lifecycles. Also, Coil depends on a number of AndroidX libraries and OkHttp which aren't (and likely won't) be multiplatform. Overall, it's a lot of work and I don't have plans to work on it. Going to leave this open as a catch-all for multiplatform questions.~

Check here for an update. TLDR: This is now planned!

colinrtwhite avatar Aug 03 '21 17:08 colinrtwhite

On compose I wrapped coil and wrote two actual functions. On desktop its look as

package components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import com.tradernet.data.source.getHttpClient
import io.ktor.client.request.*
import org.jetbrains.skija.Image
import androidx.compose.foundation.Image as ComposeImage

suspend fun loadPicture(url: String): Result<ImageBitmap> {
    val client = getHttpClient()
    return try {
        val image = client.get<ByteArray>(url)
        Result.success(Image.makeFromEncoded(image).asImageBitmap())
    } catch (e: Exception) {
        Result.failure(e)
    }
}

@Composable
actual fun ExternalImage(url: String, modifier: Modifier, OnFail: @Composable () -> Unit) {
    var isLoading by remember { mutableStateOf(false) }
    var hasFail by remember { mutableStateOf(false) }
    var imageBitmap: ImageBitmap? by remember { mutableStateOf(null) }

    LaunchedEffect(url) {
        isLoading = true
        loadPicture(url)
            .onSuccess {
                imageBitmap = it
            }
            .onFailure {
                hasFail = true
            }
        isLoading = false
    }

    when {
        isLoading -> {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                CircularProgressIndicator()
            }
        }
        hasFail -> {
            OnFail()
        }
        else -> {
            imageBitmap?.let { bitmap ->
                
                ComposeImage(
                    bitmap = bitmap,
                    contentDescription = "",
                    contentScale = ContentScale.FillWidth,
                    alpha = DefaultAlpha,
                    colorFilter = null,
                )

            } ?: OnFail()
        }
    }
}

it's a variant I took from https://stackoverflow.com/questions/66002696/how-to-load-image-from-remote-url-in-kotlin-compose-desktop. What's missing is caching mechanism.

jershell avatar Aug 06 '21 11:08 jershell

I too would love to see a coil-desktop implementation, even if it was just using JVM calls under the hood, similar to this post https://dev.to/gerardpaligot/sharing-compose-components-between-android-and-desktop-17kg

apkelly avatar Aug 29 '21 02:08 apkelly

Thanks @jershell . Just a note for those who will try to reuse your snippet: it was easier for me to use skiko v0.5.3 instead of skija, and then just swap the line in the try catch with:

Result.success(Image.makeFromEncoded(image).toComposeImageBitmap())

Works but yikes for the caching indeed, so +1 for Coil-desktop. Not sure if there is multiplatform alternatives or even work-in-progress on this topic, please share if you know something.

glureau avatar Nov 14 '21 23:11 glureau

In case it's relevant to the feasibility - https://github.com/square/okhttp/issues/6963

Libraries like Square Wire (gRPC) are also blocked on a multiplatform OkHttp Call.Factory. I hope in 2022, we get a good suite of KMP libraries for IO, Networking, RPC, Images, so hope this becomes feasible at some point.

yschimke avatar Jan 14 '22 12:01 yschimke

For multiplatform projects there is the Kamel, based on watch I saw of the source code, almost all the source is based on Ktor Client, Skiko and Androidx Compose, this meas that even now that is working on Desktop and don't see how hard would be to make it support on iOS in the future.

DevSrSouza avatar Jan 26 '22 18:01 DevSrSouza

Hi, since coil 2.0 do you have any plans to implement this feature?

egorikftp avatar Mar 31 '22 20:03 egorikftp

Porting the library from okhttp to ktor(which can still use okhttp) will be a great start to make this library multiplatform.

Coroutines, ktor and okio are all multiplatform libraries.

dragossusi avatar Apr 14 '22 07:04 dragossusi

Adding ktor means adding more code in Android apps with no purpose. Either Coil stick to Android and it should not try to move to ktor (and keep a lightweight performant library), or it's decided that Coil wants to go full KMP. I think a lot of Android projects will not move to KMP right now, so Coil is still a very good option as is.

Since Kamel is going multi-platform from start, maybe better to consider adding missing things to Kamel instead of trying to port a lot of Android specific code to KMP?

glureau avatar Apr 14 '22 08:04 glureau

This issue is for compose desktop which works on the jvm, not KMP, which I do think would be an easier ask.

evant avatar Apr 14 '22 14:04 evant

I am using this library for now which works great: https://github.com/alialbaali/Kamel/pull/14#issuecomment-1150598198

ln-12 avatar Jun 27 '22 11:06 ln-12

Yes desktop (jvm) version would be great, all of infrastructure as far as coroutines, okhttp etc should be there, so "only" Bitmap & friends would need to be wrapped

Compose has a ImageBitmap , maybe that is a way?

Note that if you want to write a proper multiplatform (mac, win, linux) desktop app, youre option basically is just Electron (VS Code, Spotify, Slack, etc), which is ..not great performance.

I could see a world where compose desktop could be very popular.

ursusursus avatar Feb 05 '23 01:02 ursusursus

You can use libs like https://github.com/alialbaali/Kamel or https://github.com/ltttttttttttt/load-the-image and use @jershell's idea to wrap this libs with expect/actual.

GabrielLasso avatar Feb 17 '23 14:02 GabrielLasso

there is already an image loader that use coil as foundation to load images https://github.com/qdsfdhvh/compose-imageloader

kazemcodes avatar Mar 26 '23 03:03 kazemcodes

Hi folks, big update! There are now plans to add Kotlin Multiplatform support to Coil and this will be the main feature of 3.x. I can't offer a timeline, but this is top of mind.

The plan is to leave Coil's public API largely the same (except for Android-specific classes) for an initial alpha then iterate on other public API changes we want to make for 3.x. Some of the main initial changes we'll need to make will be to abstract away usages of Bitmap and Drawable and replace BitmapFactory with a multiplatform solution. Fortunately, Compose Multiplatform should make migrating the coil-compose artifact fairly straightforward. I'm heading on holiday until May 7th, but will follow up with more details afterwards.

colinrtwhite avatar Apr 17 '23 23:04 colinrtwhite

To be clear, by Kotlin Multiplatform support do you mean support for Compose Multiplatform or some UI framework independent solution (which will be much more complicated I think)? Also what platforms do you plan to support in addition to Android?

mxalbert1996 avatar Apr 22 '23 06:04 mxalbert1996

@colinrtwhite have you considered applying for a grant for this - http://kotlinfoundation.org/grants

eygraber avatar Apr 24 '23 14:04 eygraber

@mxalbert1996 The goal is to support Kotlin Multiplatform and Compose Multiplatform with as many targets as possible. The Android target will have the most functionality by supporting lifecycles, views, and other Android concepts. Other targets will be focused on supporting fetching + decoding images with integration with coroutines and less platform-specific support.

I think we can accomplish this by replacing BitmapFactory with skiko, which is the image decoder used by Compose Multiplatform, for non-Android targets. Migrating to Compose Multiplatform should be pretty straightforward once coil-base has been converted to multiplatform. We'll just need to swap AndroidX Compose with Jetbrains Compose.

The biggest challenge I see at the moment is refactoring Context references so ImageLoader and other classes aren't coupled with Context. Currently, we pass a Context object deep into ImageLoader's classes.

colinrtwhite avatar May 11 '23 21:05 colinrtwhite

Good news! Any idea if skiko will be more or less performant than BitmapFactory on Android?

For the Context I tend to use a ContentProvider that hold a static reference to the application Context, so that it can be used in androidMain code without being declared in commonMain. Do you see any issue with this approach? Is there a limitation with theme / image loading requiring the Activity/fragment context instead?

glureau avatar May 12 '23 06:05 glureau

@colinrtwhite Don't you think about making a development branch for working on this feature so we can contribute and make this process faster

MohamedRejeb avatar May 12 '23 15:05 MohamedRejeb

@glureau My guess is it would be slightly slower given BitmapFactory is an Android framework class, which is why we should continue to use BitmapFactory on Android and skiko only on non-Android targets. For Context I'd prefer to avoid content providers and have the user pass a Context when building the ImageLoader.

@MohamedRejeb Will do! I'm going to create a 3.x branch that folks can contribute to once I've handled some of the initial multiplatform migration.

colinrtwhite avatar May 12 '23 16:05 colinrtwhite

To use BitmapFactory on Android is the right decision because Skiko is not a lightweight library 😉

terrakok avatar May 12 '23 16:05 terrakok

@colinrtwhite Is there a plan to migrate from OkHttp to Ktor for sending HTTP requests to support for Kotlin Multiplatform Mobile?

Or are you planning to use different HTTP clients depending on the environment?

sonatard avatar May 16 '23 01:05 sonatard

@sonatard That's still up for discussion. I think it's likely we migrate to Ktor and make it an optional dependency to support https://github.com/coil-kt/coil/issues/1648.

colinrtwhite avatar May 16 '23 21:05 colinrtwhite

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

sonatard avatar May 17 '23 01:05 sonatard

Most will driver ktor with okhttp

On Wed, 17 May 2023, 03:49 sonatard, @.***> wrote:

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

— Reply to this email directly, view it on GitHub https://github.com/coil-kt/coil/issues/842#issuecomment-1550570657, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALPBKTY3Y3RPIRNP6EDSODXGQVBZANCNFSM5BOPLKOA . You are receiving this because you commented.Message ID: @.***>

ursusursus avatar May 17 '23 02:05 ursusursus

Hmm, is it? Stable?

On Sat, 20 May 2023, 16:41 Natan Lifshitz, @.***> wrote:

Why migrate to ktor if okhttp already supports Kotlin Multiplatform?

— Reply to this email directly, view it on GitHub https://github.com/coil-kt/coil/issues/842#issuecomment-1555926634, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALPBKRMNRD4AMGN5XRUFR3XHDJYPANCNFSM5BOPLKOA . You are receiving this because you commented.Message ID: @.***>

ursusursus avatar May 20 '23 15:05 ursusursus

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

+1 for using ktor

lucasmeneghin avatar May 23 '23 18:05 lucasmeneghin

Hi Please pace yourself

mahramane avatar Jun 21 '23 07:06 mahramane

I've been lurking this thread, just wanted to say congratulations https://kotlinfoundation.org/news/grants-program-winners-23/

FunkyMuse avatar Jul 05 '23 11:07 FunkyMuse