Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose Multiplatform support #842

Open
tiwiz opened this issue Aug 3, 2021 · 102 comments
Open

Compose Multiplatform support #842

tiwiz opened this issue Aug 3, 2021 · 102 comments
Labels
enhancement New feature or request

Comments

@tiwiz
Copy link

tiwiz commented Aug 3, 2021

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 tiwiz added the enhancement New feature or request label Aug 3, 2021
@colinrtwhite
Copy link
Member

colinrtwhite commented Aug 3, 2021

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!

@jershell
Copy link

jershell commented Aug 6, 2021

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.

@apkelly
Copy link

apkelly commented Aug 29, 2021

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

@glureau
Copy link

glureau commented Nov 14, 2021

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.

@yschimke
Copy link
Contributor

In case it's relevant to the feasibility - square/okhttp#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.

@DevSrSouza
Copy link

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.

@egorikftp
Copy link

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

@dragossusi
Copy link

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.

@glureau
Copy link

glureau commented Apr 14, 2022

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?

@evant
Copy link

evant commented Apr 14, 2022

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

@ln-12
Copy link

ln-12 commented Jun 27, 2022

I am using this library for now which works great: Kamel-Media/Kamel#14 (comment)

@ursusursus
Copy link

ursusursus commented Feb 5, 2023

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.

@GabrielLasso
Copy link

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.

@kazemcodes
Copy link

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

@colinrtwhite
Copy link
Member

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 colinrtwhite changed the title Compose Desktop compatibility Compose Multiplatform support Apr 17, 2023
@colinrtwhite colinrtwhite pinned this issue Apr 18, 2023
@mxalbert1996
Copy link

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?

@eygraber
Copy link

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

@colinrtwhite
Copy link
Member

colinrtwhite commented May 11, 2023

@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.

@glureau
Copy link

glureau commented May 12, 2023

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?

@MohamedRejeb
Copy link
Contributor

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

@colinrtwhite
Copy link
Member

@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.

@terrakok
Copy link

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

@AmeerAmjed
Copy link

AmeerAmjed commented Feb 27, 2024

Any update???
I use Ktor version 3.0.0-wasm2 but can't install coil3, I'm try install mutli version but get error "3.0.0-alpha04-wasm" & "3.0.0-alpha04" which one support wasmjs.

Could not resolve all dependencies for configuration ':shared:jsCompileClasspath'.
Could not find io.coil-kt.coil3:coil-compose:3.0.0-alpha04-wasm.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
- https://repo.maven.apache.org/maven2/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
- https://maven.pkg.jetbrains.space/public/p/compose/dev/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
- https://s01.oss.sonatype.org/content/repositories/snapshots/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
- file:/Users/xyz/.m2/repository/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
- https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental/io/coil-kt/coil3/coil-compose/3.0.0-alpha04-wasm/coil-compose-3.0.0-alpha04-wasm.pom
Required by:
project :shared

@colinrtwhite
Copy link
Member

@AmeerAmjed 3.0.0-alpha04-wasm isn't a valid version.

@colinrtwhite
Copy link
Member

colinrtwhite commented Feb 29, 2024

3.0.0-alpha05 adds support for wasmJs! I implemented a work-around so Ktor 3.0.0-wasm2 (which isn't available on mavenCentral) is only required for the wasmJs artifact. If you don't add the wasmJs target, Coil will depend on Ktor 2.3.8.

@joreilly
Copy link

joreilly commented Mar 2, 2024

@colinrtwhite I probably misunderstood something from above but getting following when depending on 3.0.0-alpha05....do I need to add some other maven repo to resolve?

Could not resolve all dependencies for wasmJsCompileClasspath.
> Could not find io.coil-kt:coil:3.0.0-alpha05.
  Searched in the following locations:

@joreilly
Copy link

joreilly commented Mar 2, 2024

ah, I did have wrong dependency....following works

implementation("io.coil-kt.coil3:coil-compose:3.0.0-alpha05")

@joreilly
Copy link

joreilly commented Mar 2, 2024

So, it's resolving but image isn't being displayed for some reason. I'll try to dig a bit deeper later but if you do want to try it's happening in Compose for Web client in following branch

https://github.com/joreilly/PeopleInSpace/tree/dependency_updates

This is code I have

                AsyncImage(
                    modifier = Modifier.size(240.dp),
                    model = personImageUrl,
                    contentDescription = person.name
                )

@rustamsmax
Copy link

@joreilly maybe we must pass string as Uri

@romainbsl
Copy link

@joreilly I had the same issue when migrating to coil3.

Network feature has been extracted from coil-core so you need to had a network dependency
https://coil-kt.github.io/coil/upgrading_to_coil3/#network-images

Surely you need io.coil-kt.coil3:coil-network-ktor :D

@joreilly
Copy link

joreilly commented Mar 2, 2024

Thanks @romainbsl , it's working now!

Screenshot 2024-03-02 at 21 03 23

@sunny7498
Copy link

sunny7498 commented Mar 3, 2024

Getting this error in compose for desktop
image

edit - Adding kotlinx-coroutines-swing fixed this

@hakanai
Copy link

hakanai commented Mar 5, 2024

Been trying for a while today to make a better replacement for this error dialog. Good grief... it's like JetBrains have never heard of SwingX.

@DRSchlaubi
Copy link

Getting this error in compose for desktop image

edit - Adding kotlinx-coroutines-swing fixed this

That's actually documented

Been trying for a while today to make a better replacement for this error dialog. Good grief... it's like JetBrains has never heard of SwingX.

I doubt that's very important to them, as this screen is not seen very often anyways and it's probably not worth adding another library, which is deprecated, just to render that error dialog differently, which you can change using LocalWindowExceptionHandlerFactory

@hakanai
Copy link

hakanai commented Mar 6, 2024

Yeah I'm not saying to haul in the whole library for an error dialog, but to imitate it with one written in Compose which looks exactly like it.

Editing just to add, I'm seeing it on average around every 2~5 minutes while doing dev , so I'm not sure what "often" means for you.

@RecodeLiner
Copy link

Good afternoon everyone, I am translating a multiplatform project from compose image loader to kamel, and from it I decided to translate it to coil, I took the implementation on ktor and ran into a problem:
Locally in IDE everything works fine, release on android also gives images correctly, but when I make release on desktop(msi for windows) I face the problem that the same link that works well for me in ide and in browser, for some reason gives error condition

@DRSchlaubi
Copy link

Sounds like proguard maybe?

@RecodeLiner
Copy link

Yes, I thought about that too, but to be honest, I don’t even know what could cause this, because coil documentation only says about coroutines and okhttp, but they are already specified in my proguard rules file

@DRSchlaubi
Copy link

It would be useful to actually see the error,

Coil components use java services, so maybe proguard erases them

@RecodeLiner
Copy link

I read the final error and there was (throwable=java.util.ServiceConfigurationError: coil3.util.FetcherServiceLoaderTarget: Provider coil3.network.ktor.internal.KtorNetworkFetcherServiceLoaderTarget not found)
Fixed it with -keep class coil3.network.ktor.internal.*
Thanks to everyone!

@lepicekmichal
Copy link

Hi, trying to use it on iOS (alpha6) and all is good but one thing, svg. While they do load, they are all tinted black.
Am I doing something wrong? How do you install the decoder? I've got this:

setSingletonImageLoaderFactory {
    ImageLoader.Builder(it)
        .components { add(SvgDecoder.Factory()) }
        .build()
}

I'll create a separate bug if this is all it should take, thank you!

@DatL4g
Copy link

DatL4g commented Mar 17, 2024

@lepicekmichal
Known skia issue https://issues.skia.org/issues/40043341

@master-lzh
Copy link

Does anyone know how to use AsyncImage on iOS platform and apply rounded corners or circles through transformations like in android

@colinrtwhite
Copy link
Member

colinrtwhite commented Mar 21, 2024

@master-lzh You should use Modifier.clip for this. Transformations overall are obsolescenced by Compose's Modifier.graphicsLayer API.

@master-lzh
Copy link

@master-lzh You should use Modifier.clip for this. Transformations overall are obsolescenced by Compose's Modifier.graphicsLayer API.

Thanks a lot, I tried it yesterday and it's working now

@nickfaces
Copy link

nickfaces commented Mar 31, 2024

Hi! I tested 3.0.0-alpha06 on android and iOS, but I can't understand how to set the headers for setting auth cookies.

@guillermolc
Copy link

guillermolc commented Apr 20, 2024

Hi everyone i received this error on my wasm web impl
Access to fetch at ... from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

im using 3.0.0-alpha06 version on my project, any tip to solve this?

@joreilly
Copy link

Some related info here fwiw qdsfdhvh/compose-imageloader#446

@guillermolc
Copy link

Hi @joreilly i checked that but isn't working
The image loader class have a constructor that require a context and it doesn't have a lambda to add new components only can read those

@realityexpander
Copy link

realityexpander commented Apr 22, 2024

@guillermolc

Hi everyone i received this error on my wasm web impl Access to fetch at ... from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

im using 3.0.0-alpha06 version on my project, any tip to solve this?

Did you set up CORS on your server to allow the request to go thru on the browser side?

You may have to use a proxy service to access your images, like this one: https://wsrv.nl/

@mahramane
Copy link

3.0.0-beta-2-eap-932 is out and support wasm js.
please update the ktor version in coil library.

@artemyto
Copy link

3.0.0-beta-2-eap-932 is out and support wasm js.
please update the ktor version in coil library.

This version is not on Maven Central, but is from different repository (KtorEAP)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests