Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.pureswift.swiftui

import androidx.compose.ui.graphics.ImageBitmap
import java.util.Collections

// A small process-wide cache of decoded images, keyed by URL. Without it, a
// re-keyed AsyncImage (URL change, a row scrolled off then back, a re-navigated
// screen) drops its bitmap and re-fetches over the network — the spinner flashes
// every time. A hit returns the decoded bitmap synchronously, so a URL seen
// before renders with no network and no spinner.
internal object ImageCache {

private const val MAX_ENTRIES = 64

// access-ordered so the least-recently-used entry is evicted first
private val store = Collections.synchronizedMap(
object : LinkedHashMap<String, ImageBitmap>(16, 0.75f, true) {
override fun removeEldestEntry(eldest: Map.Entry<String, ImageBitmap>): Boolean =
size > MAX_ENTRIES
}
)

fun get(url: String): ImageBitmap? = store[url]

fun put(url: String, bitmap: ImageBitmap) {
store[url] = bitmap
}
}
12 changes: 10 additions & 2 deletions Demo/swiftui/src/commonMain/kotlin/com/pureswift/swiftui/Render.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1314,11 +1314,19 @@ private fun RenderAsyncImage(node: ViewNode) {
Text("[image]", modifier = node.composeModifiers())
return
}
var bitmap by remember(url) { mutableStateOf<ImageBitmap?>(null) }
// Seed from the cache synchronously: a URL already fetched renders on the
// first frame with no spinner. Only a miss touches the network.
var bitmap by remember(url) { mutableStateOf(ImageCache.get(url)) }
var failed by remember(url) { mutableStateOf(false) }
LaunchedEffect(url) {
if (bitmap != null) return@LaunchedEffect
val loaded = loadRemoteImage(url)
if (loaded != null) bitmap = loaded else failed = true
if (loaded != null) {
ImageCache.put(url, loaded)
bitmap = loaded
} else {
failed = true
}
}
val image = bitmap
when {
Expand Down
Loading