Persistent key-value store for app state.
Backed by :dets — Erlang's disk-based term storage, part of OTP stdlib.
State survives app kills and restarts. Any Elixir term can be stored as a
value; no serialisation step required.
When to use this vs. Ecto
Dala.State— app preferences, UI choices, small per-user settings. Think: selected theme, onboarding complete flag, last-opened tab, cached user ID. Designed for O(dozens) of keys, not O(thousands) of rows.Your Ecto Repo — user records, structured data, anything you'd query, filter, or paginate. Use migrations and schemas for that.
Usage
# Store anything — atoms, maps, lists, nested structures.
Dala.State.put(:theme, :citrus)
Dala.State.put(:onboarded, true)
Dala.State.put(:last_position, %{lat: 43.7, lng: -79.4})
# Read back on next launch — returns `default` if not yet set.
Dala.State.get(:theme, :obsidian) #=> :citrus
Dala.State.get(:missing_key, 0) #=> 0
# Remove a key.
Dala.State.delete(:last_position)Lifecycle
Started automatically by Dala.App.start/0 — no setup required.
The backing file is stored at dala_DATA_DIR/dala_state.dets on device,
or priv/repo/dala_state.dets in local dev (same directory as the SQLite DB).
Summary
Functions
Returns a specification to start this module under a supervisor.
Delete a key.
Read a persisted value.
Persist a key-value pair to disk.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec delete(term()) :: :ok
Delete a key.
No-op if the key is absent. The deletion is synchronised to disk before this function returns.
Dala.State.delete(:theme)
Dala.State.get(:theme, :obsidian) #=> :obsidian
Read a persisted value.
Returns default if the key has never been written. default is nil
unless explicitly provided.
iex> Dala.State.get(:theme, :obsidian)
:obsidian
iex> Dala.State.put(:theme, :citrus)
iex> Dala.State.get(:theme, :obsidian)
:citrusReads go directly to the underlying :dets table without going through
the GenServer, so they never queue behind in-flight writes.
Persist a key-value pair to disk.
Any Elixir term is accepted for both key and value. Backed by :dets
(Erlang's disk-based term storage) with a sync after each write, so the
value is on disk before this function returns and survives an immediate
SIGKILL (Android OOM kill, iOS termination under memory pressure).
Dala.State.put(:theme, :citrus)
Dala.State.put(:onboarded, true)
Dala.State.put({:last_seen, :home}, DateTime.utc_now())
Dala.State.put(:prefs, %{font_size: 16, notifications: true})Calling put/2 with an existing key overwrites the previous value.