Mob.Router (mob v0.7.39)

Copy Markdown View Source

Owns navigation, and the one process per live screen that serves it.

Which stacks exist, which is active, and one Mob.Screen.Server per live screen — this process starts them, stops them, and restarts one that crashes. It keeps the :mob_screen registered name, so the native layer's enif_whereis_pid lookups (back gesture, alert actions, launch notifications) are unaffected.

Not in the per-message path

A screen handling an ordinary message never touches this process. Native events reach a screen directly: Mob.Listener unwraps the envelope and sends to the screen's own pid, the screen renders, and Mob.Sender commits. The router hears only about navigation.

That is the property MOB-113 exists to guarantee, and it is what makes one process per screen affordable. An earlier costing of this design assumed a router in the loop and concluded per-screen processes could not escape a hop per message; splitting the router from the sender is what dissolved that.

Mob.Screen delegates its public API here, so callers keep using Mob.Screen.dispatch/3 and friends.

A navigation entry

%{module:, pid:, params:, ref:}.

params is carried because a restart has to reproduce the screen exactly — one that mounts on %{id: id} cannot come back from %{}.

ref identifies the screen to Mob.Sender, and is unique per screen, not per stack. Every screen is a live process that repaints on any message it receives, including the ones below the top of a stack; keyed by stack, a timer tick in a screen the user cannot see would commit its tree — tap table included — over the screen they can. The sender only commits the tree whose ref is active. The ref survives a restart, because the replacement is the same logical screen.

See decisions/2026-08-28-screen-processes-and-supervision.md.

Summary

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 pid of the process owning the currently active screen.

Return the current socket state of a running screen, or nil while that screen is being restarted.

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.

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() | nil}]

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

get_screen_pid(pid)

@spec get_screen_pid(GenServer.server()) :: pid()

Return the pid of the process owning the currently active screen.

Each live screen is its own process since MOB-112; this is how tooling reaches the one that is on screen.

get_socket(pid)

@spec get_socket(pid()) :: Mob.Socket.t() | nil

Return the current socket state of a running screen, or nil while that screen is being restarted.

Intended for testing and debugging — not for production app logic.

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