One process per live screen, owning that screen's socket.
Before MOB-112 a single Mob.Screen process held {module, socket, nav_history, render_mode} and swapped the first two in place on navigation.
Every screen shared one mailbox, so a crash in any handle_event took down
navigation and every other screen with it — the isolation Mob.Screen's
moduledoc claimed and mob#76 had to write around.
Mob.Router owns navigation and starts one of these per live screen. A crash
here kills this screen only; the router sees the exit, restarts it, and
re-renders.
self() means what users already assume
Inside a screen callback self() is now the screen's own pid, not the
process registered as :mob_screen. Screens already wrote
on_tap: {self(), :save} and started tasks expecting exactly that; before,
those resolved to the one shared process, which is what let a task started by
screen A be delivered into screen B's handle_info with B's socket
(MOB-107).
A restart re-mounts
A restarted screen runs mount/3 again and loses its assigns. Persisted
screens (use Mob.Screen, vsn: N or persist: true) get their dumped state
back through load_state/2; everything else starts fresh. Stated rather than
implied, because it is the visible consequence of the isolation: the screen
survives, its in-memory state does not.
Navigation is not this process's business
A user callback that sets a nav action — push_screen/2, pop_screen/1,
switch_tab/2 — has that action handed to the owner, and this process does
not paint. The owner decides which screen is current and tells that
screen to paint; painting here would flash this screen's tree for a frame
before the navigation replaced it. Ordinary messages never reach the owner,
which is what keeps it off the hot path (MOB-113).
Summary
Types
Identifies this screen to Mob.Sender. Unique per screen, not per stack.
Functions
Returns a specification to start this module under a supervisor.
Run a user event, returning any navigation action it produced.
Repaint with the screen module's newly loaded code.
Paint this screen, with the given navigation transition.
Paint and block until the frame has been committed.
This screen's current socket.
Start a screen linked to the calling process.
Render this screen's tree, in this screen's process.
Types
@type render_ref() :: reference()
Identifies this screen to Mob.Sender. 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 they would share a ref, and a timer tick in a screen the user cannot see would commit its tree over the one they can. The sender only commits the active ref.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Run a user event, returning any navigation action it produced.
@spec hot_reload(pid()) :: :ok
Repaint with the screen module's newly loaded code.
Paint this screen, with the given navigation transition.
Paint and block until the frame has been committed.
@spec socket(pid()) :: Mob.Socket.t()
This screen's current socket.
@spec start_link(keyword()) :: GenServer.on_start()
Start a screen linked to the calling process.
:owner receives nav actions and the exit signal. :ref identifies this
screen to Mob.Sender and is unique per screen — see render_ref/0.
Mob.Router links and traps exits. Linking alone would make the owner die
with any screen it stopped or that crashed; trapping alone would leave every
screen orphaned when the owner died — and an orphaned persisted screen keeps
dumping to Mob.ScreenState under the same key as its live replacement.
Together the owner observes each exit as a message without sharing its fate,
and screens still come down with it.
Render this screen's tree, in this screen's process.
For inspection only — it does not commit anything. Running render/1 in the
caller instead would put user code in the owner, where a raise takes down
navigation and every other screen.