Search documentation of mob

# Mob.Screen behaviour (mob v0.6.18)

Copy Markdown View Source

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
    end

## Starting a screen

    {:ok, pid} = Mob.Screen.start_link(MyApp.CounterScreen, %{})

## Dispatching events

    :ok = Mob.Screen.dispatch(pid, "increment", %{})

## Summary

### Types

socket()

### Callbacks

dump_state(assigns)

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).

handle_event(event, params, socket)
handle_info(message, socket)
load_state(stored_vsn, stored)

Reconstruct assigns from a previously persisted map.

mount(params, session, socket)
render(assigns)
screen_key(assigns)

Return a stable string key for storing this screen's state.

terminate(reason, socket)

### Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

dispatch(pid, event, params)

Dispatch a UI event to the screen process. Returns `:ok` synchronously once the event has been processed and the state updated.

get_current_module(pid)

Return the module of the currently active screen in the navigation stack. Intended for testing and debugging.

get_nav_history(pid)

Return the navigation history (list of `{module, socket}` pairs, head = most recent). Intended for testing and debugging.

get_socket(pid)

Return the current socket state of a running screen. Intended for testing and debugging — not for production app logic.

handle_call(msg, from, state)

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_link(screen_module, params, opts \\ [])

Start a screen process linked to the calling process.

start_root(screen_module, params \\ %{}, opts \\ [])

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

# socket()

    @type socket() :: Mob.Socket.t()

## Callbacks

# dump_state(assigns)

    @callback dump_state(assigns :: map()) :: map()

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`.

# handle_event(event, params, socket)

(optional)

    @callback handle_event(event :: String.t(), params :: map(), socket :: socket()) ::
      {:noreply, socket()} | {:reply, map(), socket()}

# handle_info(message, socket)

(optional)

    @callback handle_info(message :: term(), socket :: socket()) :: {:noreply, socket()}

# load_state(stored_vsn, stored)

    @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.

# mount(params, session, socket)

    @callback mount(params :: map(), session :: map(), socket :: socket()) ::
      {:ok, socket()} | {:error, term()}

# render(assigns)

    @callback render(assigns :: map()) :: map()

# screen_key(assigns)

(optional)

    @callback screen_key(assigns :: map()) :: String.t()

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.

# terminate(reason, socket)

(optional)

    @callback terminate(reason :: term(), socket :: socket()) :: term()

## Functions

# child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# dispatch(pid, event, params)

    @spec dispatch(pid(), String.t(), map()) :: :ok

Dispatch a UI event to the screen process. Returns `:ok` synchronously once the event has been processed and the state updated.

# get_current_module(pid)

    @spec get_current_module(pid()) :: module()

Return the module of the currently active screen in the navigation stack. Intended for testing and debugging.

# get_nav_history(pid)

    @spec get_nav_history(pid()) :: [{module(), Mob.Socket.t()}]

Return the navigation history (list of `{module, socket}` pairs, head = most recent). Intended for testing and debugging.

# get_socket(pid)

    @spec get_socket(pid()) :: socket()

Return the current socket state of a running screen. Intended for testing and debugging — not for production app logic.

# handle_call(msg, from, state)

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

# start_link(screen_module, params, opts \\ [])

    @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`.

# start_root(screen_module, params \\ %{}, opts \\ [])

    @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).