Documentation

$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:

MechanismScopeReach for it when
Navigation stateOne navigationThe next screen needs a title and image immediately, so it can render before its fetch lands
Route cachePer route + paramsAutomatic. Revisiting a screen is instant until $invalidate()
@storeWhole appThe data is a real concern with its own logic — a session, a cart
$cacheWhole appPlain 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.

See Also

  • $data — loaders, invalidation, route caching
  • $fetch — disk caching with a TTL
  • @store — app-wide state with behavior attached