Enables one or more optional Mob features by patching mix.exs, manifest
files, and generating any required source files.
Usage
mix mob.enable FEATURE [FEATURE ...]Multiple features can be enabled in a single command:
mix mob.enable camera photo_library
mix mob.enable camera photo_library file_sharing liveviewFeatures
liveview
Enables LiveView mode — the Mob app runs a local Phoenix endpoint and displays it in a native WebView. Web developers can ship a mobile app with zero native UI code.
What it does:
- Generates
lib/<app>/mob_screen.ex— aMob.Screenthat opens a WebView athttp://127.0.0.1:PORT/ - Injects the
MobHookLiveView hook intoassets/js/app.js - Injects a hidden
<div id="mob-bridge" phx-hook="MobHook">intoroot.html.heex— this is required for the hook to mount - Updates
mob.exswithliveview_portsoMob.LiveView.local_url/1works
Why the hidden div is required
Phoenix LiveView hooks only execute when a DOM element carrying
phx-hook="MobHook" exists in the rendered page. Registering MobHook in
app.js is necessary but not sufficient — without a matching DOM element the
hook never mounts and window.mob is never replaced with the LiveView-backed
version. Messages would silently route through the native NIF bridge instead
of the LiveView WebSocket, so handle_event/3 would never fire.
See MobDev.Enable module doc and guides/liveview.md for the full
two-bridge architecture explanation.
After running:
- Add
MyApp.MobScreento your supervision tree (or callMob.Screen.start_root(MyApp.MobScreen)from yourMob.App.on_start/0) - Ensure Phoenix is running on the port set in
mob.exs(default: 4000)
camera
Adds camera permission declarations to platform manifests.
- iOS: adds
NSCameraUsageDescriptiontoios/*/Info.plist - Android: adds
<uses-permission android:name="android.permission.CAMERA"/>toandroid/app/src/main/AndroidManifest.xml
photo_library
- iOS: adds
NSPhotoLibraryAddUsageDescriptionto Info.plist - Android: no manifest change needed (API 29+)
file_sharing
- iOS: adds
UIFileSharingEnabledandLSSupportsOpeningDocumentsInPlaceto Info.plist - Android: adds
<provider android:name="FileProvider">with paths config
location
- iOS: adds
NSLocationWhenInUseUsageDescriptionto Info.plist - Android: adds
ACCESS_FINE_LOCATIONpermission
notifications
- iOS: creates
ios/<app>.entitlementswithaps-environment: development. After running, executemix mob.provisionso Xcode downloads a push-capable provisioning profile. Then callMob.Permissions.request(socket, :notifications)andMob.Notify.register_push(socket)at runtime to obtain a device token. - Android: runtime only —
POST_NOTIFICATIONSis requested at runtime, no manifest key needed.
pythonx
Enables embedded CPython via Pythonx on iOS and Android.
- iOS: BeeWare's
Python-Apple-supportPython.xcframeworkis bundled bymix mob.deploy --native. - Android: Chaquopy's prebuilt CPython
is unpacked at first launch.
libpythonx.so(the Pythonx NIF) is cross-compiled with the Android NDK against a stublibpython3.13.soso the BEAM dynamic loader is satisfied; the real lib resolves at runtime via SONAME match. - Bare CPython only. Bundles ship the interpreter, stdlib, and
standard C extensions (
_ssl,_ctypes,_hashlib, …). Third-party wheels (cryptography,numpy,RNS, …) are out of scope — produce your own (BeeWare'smobile-forgeon iOS, Chaquopy's wheel pipeline on Android) and drop them into your project.
What it does:
- Adds
{:pythonx, "~> 0.4"}tomix.exsdeps. - Generates
lib/<app>/python_paths.ex— pure detection module that locates the bundled framework at runtime (:desktop/{:ios, paths}/{:android, paths}/{:partial, missing}). - No
:uv_initconfig patch. Pythonx ships an Application that auto-runs uv at boot if:uv_initis in compile-time config, and uv doesn't exist on device. Instead the on_start template inlinespyproject_tomland callsPythonx.Uv.fetch/2 + Pythonx.Uv.init/2only on the:desktopbranch. Same code pathiex -S mixwould use, just opt-in.
Bundle size impact: ~70 MB on iOS, ~30 MB on Android (interpreter + stdlib + arch-specific C extensions). Apply this only when you actually want to call Python from BEAM — non-Python apps stay vanilla.
After running, your Mob.App.on_start/0 should:
- call
Application.ensure_all_started(:pythonx)(starts thePythonx.Janitor, required forPythonx.eval/3); - case-match
<App>.PythonPaths.detect/1and callPythonx.Uv.fetch + initon:desktop(provisioning a uv-managed CPython on first run) orPythonx.init/4on{:ios, _}/{:android, _}.
See guides/python_embedding.md for the full template.
mlx
Enables Apple's MLX library + the EMLX Nx backend on iOS. Gives the app fast on-device tensor math (matmul, FFT, linalg, etc.) backed by Apple's Accelerate framework (vectorized BLAS/LAPACK).
- iOS device + simulator:
libmlx.a+libemlx.aare cross-compiled and statically linked into the app binary. The pre-built bundle (~5 MB compressed, ~30 MB on disk per arch) is downloaded once and cached at~/.mob/cache/libmlx-<ver>-ios-<slice>/byMobDev.MLXDownloader.MOB_STATIC_EMLX_NIFflips on automatically — the EMLX NIF is registered in the static-NIF table soload_nif/2resolves it without dlopen. - Android: not supported in v1. No Metal on Android — a CPU-only NDK build via OpenBLAS is the path forward but isn't shipped yet.
- CPU-only for v1. Metal-on-iOS needs the iOS-Metal CMakeLists patch and Xcode 16's optional Metal Toolchain — deferred to a v2 tarball variant.
What it does:
- Adds
{:nx, "~> 0.10"}and{:emlx, "~> 0.2"}tomix.exsdeps. - Generates
lib/<app>/ml_init.ex— a one-call helper that setsEMLX.Backendas Nx's global default, with a cleanNx.BinaryBackendfallback if the NIF can't load.
After running, your Mob.App.on_start/0 should call
<App>.MLInit.configure() once Mob.Screen.start_root/1 and
Mob.Dist.ensure_started/1 have run.