Behaviour and GenServer wrapper for a Mob screen.
Each screen runs as a supervised GenServer whose state is a Mob.Socket.
Putting one process per screen — instead of one big process for the whole
app — gives you isolation: a buggy handle_event crashes its own screen
and the supervisor restarts it without taking down navigation, audio,
background services, or the BEAM itself. Lifecycle callbacks (mount,
render, handle_event, handle_info, terminate) map directly to the
GenServer lifecycle, so the BEAM's existing concurrency tools (selective
receive, monitors, hot code push) work on screens without any Mob-specific
scaffolding.
Usage
defmodule MyApp.CounterScreen do
use Mob.Screen
def mount(_params, _session, socket) do
{:ok, Mob.Socket.assign(socket, :count, 0)}
end
def render(assigns) do
%{
type: :column,
props: %{},
children: [
%{type: :text, props: %{text: "Count: #{assigns.count}"}, children: []}
]
}
end
def handle_event("increment", _params, socket) do
{:noreply, Mob.Socket.assign(socket, :count, socket.assigns.count + 1)}
end
endStarting a screen
{:ok, pid} = Mob.Screen.start_link(MyApp.CounterScreen, %{})Dispatching events
:ok = Mob.Screen.dispatch(pid, "increment", %{})
Summary
Callbacks
Serialise assigns for persistence. Return a plain map of the keys you want restored on next launch. Defaults to the full assigns map minus any non-serialisable values (PIDs, references, ports, functions).
Reconstruct assigns from a previously persisted map.
Return a stable string key for storing this screen's state.
Functions
Returns a specification to start this module under a supervisor.
Dispatch a UI event to the screen process. Returns :ok synchronously once
the event has been processed and the state updated.
Return the module of the currently active screen in the navigation stack. Intended for testing and debugging.
Return the navigation history (list of {module, socket} pairs, head = most recent).
Intended for testing and debugging.
Return the current socket state of a running screen. Intended for testing and debugging — not for production app logic.
Apply a navigation action directly. Used by Mob.Test to drive navigation
programmatically without needing a UI event. Synchronous — the caller blocks
until the navigation (and re-render, in production mode) completes.
Start a screen process linked to the calling process.
Start a screen as the root UI screen. Calls mount, renders the component tree
via Mob.Renderer, and calls set_root on the resulting view.
Types
@type socket() :: Mob.Socket.t()
Callbacks
Serialise assigns for persistence. Return a plain map of the keys you want restored on next launch. Defaults to the full assigns map minus any non-serialisable values (PIDs, references, ports, functions).
Only called when use Mob.Screen, vsn: N (N > 0) or persist: true.
@callback load_state(stored_vsn :: non_neg_integer(), stored :: map()) :: map()
Reconstruct assigns from a previously persisted map.
stored_vsn is the version that was current when the data was saved.
Match on it to migrate old shapes:
def load_state(1, stored), do: stored
def load_state(0, stored), do: Map.put(stored, :new_field, :default)The returned map is merged into the socket's assigns after mount/3 runs.
Only called when stored data exists.
Return a stable string key for storing this screen's state.
Implement when the same screen module holds per-user or parameterised state:
def screen_key(assigns), do: "#{__MODULE__}:#{assigns.user_id}"Defaults to the module name string.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Dispatch a UI event to the screen process. Returns :ok synchronously once
the event has been processed and the state updated.
Return the module of the currently active screen in the navigation stack. Intended for testing and debugging.
Return the current socket state of a running screen. Intended for testing and debugging — not for production app logic.
Apply a navigation action directly. Used by Mob.Test to drive navigation
programmatically without needing a UI event. Synchronous — the caller blocks
until the navigation (and re-render, in production mode) completes.
Valid actions mirror the Mob.Socket navigation functions:
{:push, dest, params}— push a new screen{:pop}— pop to the previous screen{:pop_to, dest}— pop to a specific screen in history{:pop_to_root}— pop to the root of the current stack{:reset, dest, params}— replace the entire nav stack
@spec start_link(module(), map(), keyword()) :: GenServer.on_start()
Start a screen process linked to the calling process.
params is passed as the first argument to mount/3.
@spec start_root(module(), map(), keyword()) :: GenServer.on_start()
Start a screen as the root UI screen. Calls mount, renders the component tree
via Mob.Renderer, and calls set_root on the resulting view.
This is the main entry point for production use. start_link/2 is for tests
(no NIF calls).