Mob — BEAM-on-device mobile framework for Elixir.
OTP runs on the device. Screens are GenServers. The UI is rendered by Compose (Android) and SwiftUI (iOS) via a thin NIF. No server required.
Quick start
defmodule MyApp.HomeScreen do
use Mob.Screen
def mount(_params, _session, socket) do
{:ok, Mob.Socket.assign(socket, :title, "Hello, Mob!")}
end
def render(assigns) do
%{
type: :column,
props: %{padding: :space_md},
children: [
%{type: :text, props: %{text: assigns.title, text_size: :xl}, children: []}
]
}
end
endModules
Mob.App— app entry point and navigation declarationMob.Screen— screen behaviour and GenServer wrapperMob.Socket— assigns and navigation APIMob.Theme— design token systemMob.Renderer— component tree serialisationMob.Test— live device inspection and testing helpers
See the Getting Started guide to create your first app. See Architecture & Prior Art for how Mob compares to LiveView Native, Elixir Desktop, React Native, Flutter, and native development.
Summary
Functions
A writable, app-private directory for runtime data — DB files, caches, downloaded assets, anything you write at runtime.
Like data_dir/0 but returns (and creates) the sub directory beneath it,
e.g. Mob.data_dir("audio_cache").
Functions
See Mob.Socket.assign/2.
See Mob.Socket.assign/3.
@spec data_dir() :: String.t()
A writable, app-private directory for runtime data — DB files, caches, downloaded assets, anything you write at runtime.
On device this is MOB_DATA_DIR, set by the BEAM launcher to the platform's
persistent app-private location (iOS NSDocumentDirectory, Android
getFilesDir()). Off device (host/dev/tests) it falls back to $HOME, then
the current working directory. The directory is created if missing.
Use this — not MOB_BEAMS_DIR. MOB_BEAMS_DIR points inside the signed,
read-only .app bundle on iOS, so writing there fails with :eperm; it
happens to be writable on Android, which is how that trap stays hidden until
an app ships to iOS.
path = Path.join(Mob.data_dir(), "my.db")See data_dir/1 for a created subdirectory.
Like data_dir/0 but returns (and creates) the sub directory beneath it,
e.g. Mob.data_dir("audio_cache").