Skip to content

Commit f523ed6

Browse files
authored
Merge pull request #65 from PureSwift/feature/asyncimage-cache
2 parents 8223c61 + 3ac7470 commit f523ed6

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.pureswift.swiftui
2+
3+
import androidx.compose.ui.graphics.ImageBitmap
4+
import java.util.Collections
5+
6+
// A small process-wide cache of decoded images, keyed by URL. Without it, a
7+
// re-keyed AsyncImage (URL change, a row scrolled off then back, a re-navigated
8+
// screen) drops its bitmap and re-fetches over the network — the spinner flashes
9+
// every time. A hit returns the decoded bitmap synchronously, so a URL seen
10+
// before renders with no network and no spinner.
11+
internal object ImageCache {
12+
13+
private const val MAX_ENTRIES = 64
14+
15+
// access-ordered so the least-recently-used entry is evicted first
16+
private val store = Collections.synchronizedMap(
17+
object : LinkedHashMap<String, ImageBitmap>(16, 0.75f, true) {
18+
override fun removeEldestEntry(eldest: Map.Entry<String, ImageBitmap>): Boolean =
19+
size > MAX_ENTRIES
20+
}
21+
)
22+
23+
fun get(url: String): ImageBitmap? = store[url]
24+
25+
fun put(url: String, bitmap: ImageBitmap) {
26+
store[url] = bitmap
27+
}
28+
}

Demo/swiftui/src/commonMain/kotlin/com/pureswift/swiftui/Render.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,11 +1314,19 @@ private fun RenderAsyncImage(node: ViewNode) {
13141314
Text("[image]", modifier = node.composeModifiers())
13151315
return
13161316
}
1317-
var bitmap by remember(url) { mutableStateOf<ImageBitmap?>(null) }
1317+
// Seed from the cache synchronously: a URL already fetched renders on the
1318+
// first frame with no spinner. Only a miss touches the network.
1319+
var bitmap by remember(url) { mutableStateOf(ImageCache.get(url)) }
13181320
var failed by remember(url) { mutableStateOf(false) }
13191321
LaunchedEffect(url) {
1322+
if (bitmap != null) return@LaunchedEffect
13201323
val loaded = loadRemoteImage(url)
1321-
if (loaded != null) bitmap = loaded else failed = true
1324+
if (loaded != null) {
1325+
ImageCache.put(url, loaded)
1326+
bitmap = loaded
1327+
} else {
1328+
failed = true
1329+
}
13221330
}
13231331
val image = bitmap
13241332
when {

0 commit comments

Comments
 (0)