Packages
mob
0.3.9
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/socket.ex
defmodule Mob.Socket do
@moduledoc """
The socket struct passed through all Mob.Screen and Mob.Component callbacks.
Holds two things:
- `assigns` — the public data map your `render/1` function reads from `@assigns`
- `__mob__` — internal Mob metadata (screen module, platform, view refs, nav stack)
You interact with a socket via `assign/2` and `assign/3`. Never mutate `__mob__`
directly — it is an internal contract.
"""
@type platform :: :android | :ios
@type t :: %__MODULE__{
assigns: map(),
__mob__: %{
screen: module() | nil,
platform: platform(),
root_view: term(),
view_tree: map(),
nav_stack: list(),
nav_action: term()
}
}
defstruct assigns: %{},
__mob__: %{
screen: nil,
platform: :android,
root_view: nil,
view_tree: %{},
nav_stack: [],
nav_action: nil
}
@doc """
Create a new socket for the given screen module.
Options:
- `:platform` — `:android` (default) or `:ios`
"""
@spec new(module(), keyword()) :: t()
def new(screen, opts \\ []) do
platform = Keyword.get(opts, :platform, :android)
%__MODULE__{
assigns: %{},
__mob__: %{
screen: screen,
platform: platform,
root_view: nil,
view_tree: %{},
nav_stack: [],
nav_action: nil
}
}
end
@doc """
Assign a single key/value pair into the socket's assigns.
socket = assign(socket, :count, 0)
"""
@spec assign(t(), atom(), term()) :: t()
def assign(%__MODULE__{assigns: assigns} = socket, key, value) when is_atom(key) do
%{socket | assigns: Map.put(assigns, key, value)}
end
@doc """
Assign multiple key/value pairs at once from a keyword list or map.
socket = assign(socket, count: 0, name: "test")
socket = assign(socket, %{count: 0})
"""
@spec assign(t(), keyword() | map()) :: t()
def assign(%__MODULE__{assigns: assigns} = socket, kw) when is_list(kw) or is_map(kw) do
%{socket | assigns: Map.merge(assigns, Map.new(kw))}
end
@doc """
Store the root view ref returned by the renderer into `__mob__.root_view`.
Called internally after the initial render.
"""
@spec put_root_view(t(), term()) :: t()
def put_root_view(%__MODULE__{__mob__: mob} = socket, ref) do
%{socket | __mob__: %{mob | root_view: ref}}
end
@doc false
@spec put_mob(t(), atom(), term()) :: t()
def put_mob(%__MODULE__{__mob__: mob} = socket, key, value) do
%{socket | __mob__: Map.put(mob, key, value)}
end
# ── Navigation API ────────────────────────────────────────────────────────
@doc """
Push a new screen onto the navigation stack.
`dest` is either a registered atom name (e.g. `:counter`) or a screen module
(e.g. `MobDemo.CounterScreen`). `params` are passed to the new screen's
`mount/3` as the first argument.
The push is applied after the current callback returns — `do_render` in
`Mob.Screen` detects the nav_action and mounts the new module.
"""
@spec push_screen(t(), atom() | module(), map()) :: t()
def push_screen(socket, dest, params \\ %{}) do
put_mob(socket, :nav_action, {:push, dest, params})
end
@doc """
Pop the current screen, returning to the previous one.
No-op if already at the root of the stack.
"""
@spec pop_screen(t()) :: t()
def pop_screen(socket) do
put_mob(socket, :nav_action, {:pop})
end
@doc """
Pop the stack until the screen registered under `dest` is at the top.
`dest` is a registered atom name or module. No-op if not found in history.
"""
@spec pop_to(t(), atom() | module()) :: t()
def pop_to(socket, dest) do
put_mob(socket, :nav_action, {:pop_to, dest})
end
@doc """
Pop to the root of the current navigation stack.
"""
@spec pop_to_root(t()) :: t()
def pop_to_root(socket) do
put_mob(socket, :nav_action, {:pop_to_root})
end
@doc """
Replace the entire navigation stack with a single new screen.
Used for auth transitions (post-login → home with no back button to login).
"""
@spec reset_to(t(), atom() | module(), map()) :: t()
def reset_to(socket, dest, params \\ %{}) do
put_mob(socket, :nav_action, {:reset, dest, params})
end
@doc """
Switch to the named tab in a tab_bar or drawer layout.
"""
@spec switch_tab(t(), atom()) :: t()
def switch_tab(socket, tab) when is_atom(tab) do
put_mob(socket, :nav_action, {:switch_tab, tab})
end
end