$cache
A process-wide key-value store, for the times a screen already has the data the next screen is about to fetch.
Reading and writing
Assign to a key, read it back with a type:
$cache('shows') = shows.associateBy { it.id }
val cached: Map<String, Show>? = $cache('shows') Reads are nullable, because the cache is memory that the process can lose. Treat a miss as normal and fall back to loading.
$cache.remove('shows')
$cache.clear() The pattern it exists for
A list screen fetches shows. Tapping one opens a detail screen that needs the same show. The detail loader can start from what the list already has, then fill in the rest:
// list +screen.load.wh
suspend fun load(): List<Show> {
val shows: List<Show> = $fetch.get(url = "…/shows")
$cache('shows') = shows.associateBy { it.id }
return shows
} // detail +screen.load.wh
suspend fun load(): ShowDetail {
val cached: Map<String, Show>? = $cache('shows')
val preview = cached?.get($screen.params.id)
return ShowDetail(preview, $fetch.get(url = "…/shows/${$screen.params.id}"))
} $cache is memory only. It does not survive the process being killed, and it is
not a place to keep anything the app needs on next launch. For that, use $fetch's disk cache, or write it somewhere yourself with $appContext.The other three ways to move data between screens
$cache is the loosest of four, and usually not the first one to reach for:
| Mechanism | Scope | Reach for it when |
|---|---|---|
| Navigation state | One navigation | The next screen needs a title and image immediately, so it can render before its fetch lands |
| Route cache | Per route + params | Automatic. Revisiting a screen is instant until $invalidate() |
| @store | Whole app | The data is a real concern with its own logic — a session, a cart |
$cache | Whole app | Plain reuse, no logic, and losing it is harmless |
Navigation state, for comparison
Handing a couple of fields forward is often all you need, and it does not need a cache at all:
$navigate("/show/${id}", state = { name: show.name, poster: show.poster })
// in the destination's loader
val previewName = $route.state?.name $defer
The related trick for making a screen appear sooner: mark the slow half of a loader deferred and it does not block the render.
suspend fun load(): ShowData {
val show = $fetch.get(url = "…") // blocks
val episodes = $defer { $fetch.get(url = "…/episodes") } // does not
return ShowData(show, episodes)
} The screen renders with episodes null and recomposes when it arrives. See $data / $layoutData.
See Also
- $data / $layoutData — loaders, invalidation, route caching
- $fetch — disk caching with a TTL
- @store — app-wide state with behavior attached