Incular logoIncular
API ReferenceApplication

Restoration

Keys, scopes, persistent values, stores, and window restoration.

Typed persistent values over Signals. Missing, corrupt, or incompatible snapshots fall back to defaults and count as stale.

TypePurpose
RestorationKeyOne path segment; rejects empty/NUL
RestorationScopeNamespaced value bucket with JSON get/set
Restorable<T>Typed persistent value over a Signal
RestorationHandleReset, flush, claim scopes, diagnostics
RestorationConfigApp id, schema version, store, limits, migration
InMemoryRestorationStoreEphemeral store for tests
FileRestorationStoreAtomic file store for production

Keys and scopes

One RestorationKey is one path segment (/ is content, not a separator).

A restorable counter — close and relaunch to see the value survive. Copy, paste, and run:

use incular::prelude::*;

fn main() {
    let config = RestorationConfig::file_backed("com.example.counter", 1)
        .expect("restoration config");
    let app = Application::new_with_restoration(
        WindowOptions::default(),
        config,
        |cx| {
            let key = RestorationKey::new("count").expect("key");
            let root: Widget = match cx.restored_signal(key, 0u32) {
                Some(count) => {
                    let tapped = count.clone();
                    Container::builder()
                        .padding(EdgeInsets::all(24.0))
                        .alignment(Alignment::CENTER)
                        .child(
                            GestureDetector::new(Text::new(format!(
                                "Count: {}",
                                count.get()
                            )))
                            .on_tap(move || tapped.update(|c| *c += 1)),
                        )
                        .build()
                        .into()
                }
                None => Text::new("Restoration off").into(),
            };
            root
        },
    )
    .expect("valid application");
    incular::run(app).expect("run application");
}

Persistent values

get() reads, set() writes equality-checked, update() mutates, remove() drops the entry, signal() exposes the underlying Signal (always write through set/update so persistence fires) — all exercised in the runnable program above.

TextEditingController::restored(scope, key) and Form::register_with_restoration(controller, scope, key) bind editors and fields the same way.

Stores and config

InMemoryRestorationStore::new() for tests; FileRestorationStore::new(path) or for_application(id) (OS dir + restoration.json) for production; RestorationConfig::new(id, version, store) or file_backed(id, version) with with_snapshot_limit (.max(1), default 8 MiB), with_debounce, and with_migration — the file_backed form runs in the program above.

Saves debounce (250 ms), write atomically (temp + rename; failures never clobber good files), retry 3x with 500 ms linear backoff.

Handle and diagnostics

Inside a build, cx.restoration() returns an optional handle; handle.reset(), handle.flush(), handle.remove_scope(&scope), handle.claim_scope(&scope), handle.diagnostics(), and handle.path() manage scopes and diagnostics.

ClaimedRestorationScope is an RAII lease; dropping releases the path.

RestorationStore trait (load / save / remove, optional location; RestorationStoreError carries diagnostics and must leave prior snapshots intact). RestorableWindowFactory is an in-memory factory for persisted auxiliary window kinds (never serialized). RootRestorationScope / UnmanagedRestorationScope bracket restoration subtrees explicitly.

Windows and routes

Restorable windows use WindowRestorationId::new("main"), Application::new_restorable, register_restorable_window_factory, and restore_restorable_windows (unknown kinds/ids/duplicates skipped and counted); route stacks persist via RestorableRoute and NavigationRestoreReport.

Related: Application, Signal, EditableText.

On this page