$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
$cache is an object with four methods, ordinary calls, the way Svelte's setContext/getContext are:
$cache.set("shows", shows.associateBy { it.id })
val cached: Map<String, Show>? = $cache.get("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.set("shows", shows.associateBy { it.id })
return shows
} // detail +screen.load.wh
suspend fun load(): ShowDetail {
val cached: Map<String, Show>? = $cache.get("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 |
A fifth mechanism, $context, is scoped to the tree rather than the whole app: each
element that sets a key provides it to its own subtree, so two instances of the same component
on one screen each get their own value. Reach for it when several instances must not collide;
reach for $cache when there is only ever one value worth sharing.
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 = $screen.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.