Packages
mob
0.9.18
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.39
0.7.38
0.7.37
0.7.36
0.7.35
0.7.34
0.7.33
0.7.32
0.7.31
0.7.30
0.7.29
0.7.28
0.7.27
0.7.26
0.7.25
0.7.24
0.7.23
0.7.22
0.7.21
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
guides/native_extensions.md
# Native Extensions
Mob apps can be extended with native code in four ways, accessed
through two Mix tasks. This guide is a summary — the detailed
contract per backend (how Cargo / Zigler / Pythonx normally work,
what Mob changes for static linking, where the bundled Python
runtime comes from on each platform, which workarounds are
transient) lives in
[mob_dev's `guides/nifs.md`](https://hexdocs.pm/mob_dev/nifs.html).
Read that one before debugging a native build.
## Two tasks, one decision
| Question | Use |
|---|---|
| "I want to write a NIF I'll name myself." | `mix mob.add_nif <name>` |
| "I want to enable a pre-named Mob feature." | `mix mob.enable <feature>` |
The split tracks a real distinction. `add_nif` creates *instances*
the user names (`audio_engine`, `image_codec`, `crypto_utils`) and
can have many of. `enable` toggles *singleton features* with fixed
implementations (`pythonx`, `mlx`, `camera`, `notifications`) — each
exists at most once per app.
## `mix mob.add_nif <name>`
Scaffolds a statically-linked NIF: Elixir stub, native skeleton
appropriate to the chosen backend, `:static_nifs` entry in `mob.exs`,
and regenerated dispatch table — one command, one diff.
```bash
mix mob.add_nif audio_engine # Elixir-only stub; you wire native side
mix mob.add_nif audio_engine --type c # also drops c_src/audio_engine.c
mix mob.add_nif audio_engine --type rustler # native/audio_engine/ Cargo crate + :rustler dep
mix mob.add_nif audio_engine --type zigler # ~Z sigil in the stub + :zigler dep
mix mob.add_nif audio_engine --type rustler --demo # also generates a demo screen
```
Why static linking? iOS App Store rejects bundled `.dylib`; Android
`RTLD_LOCAL` hides the parent's `enif_*` symbols from a `dlopen`'d
child. Both platforms force the same answer: link the NIF init
function into the main app binary alongside `libbeam.a`. mob_dev
handles the cross-compile and link automatically — you write the
Rust/Zig/C, run `mix mob.deploy --native`, and the right `.a` ends
up in the right place per arch.
**Bringing in an existing Rust project** (one crate or many — there's
no upper limit) takes four manual steps documented in
[mob_dev's NIF guide](https://hexdocs.pm/mob_dev/nifs.html#bringing-in-an-existing-rust-crate).
You don't need to be a Rust expert to follow it — the steps are
copy-paste.
## `mix mob.enable <feature>`
Toggles an optional feature with a fixed implementation. Patches
`mix.exs`, platform manifests (Info.plist / AndroidManifest.xml),
and any required source files in one Igniter run.
| Feature | What it gives you |
|---|---|
| `liveview` | Phoenix LiveView mode — app renders a local web view |
| `camera` | Camera permission + capture API |
| `photo_library` | Photo picker + saving |
| `file_sharing` | iOS Files-app integration + Android FileProvider |
| `location` | Coarse + fine location permissions and API |
| `notifications` | Push notifications (entitlement + APNs / FCM glue) |
| `pythonx` | Embedded CPython interpreter on iOS + Android |
| `mlx` | Apple MLX tensor math + EMLX Nx backend (iOS) |
```bash
mix mob.enable camera photo_library # multiple in one command
mix mob.enable pythonx # embeds CPython 3.13 on both platforms
mix mob.enable mlx # on-device tensor math (iOS, ~30 MB)
```
The `pythonx` and `mlx` features cost real bundle size (~70 MB and
~30 MB respectively). The rest are cheap (manifest entries + a few
hundred lines of generated Elixir/Swift/Kotlin).
For exactly where Mob fetches the bundled CPython runtime from
(BeeWare's `Python-Apple-support` for iOS, Chaquopy for Android, why
two sources, what's identical between them) — see the Pythonx
section of [mob_dev's NIF guide](https://hexdocs.pm/mob_dev/nifs.html#python-via-pythonx).
## What gets generated, where
For any NIF added via `mob.add_nif <name>` (regardless of `--type`):
```
lib/<app>/nifs/<name>.ex # Elixir stub module
mob.exs # :static_nifs entry appended
priv/generated/driver_tab_ios.zig # dispatch table (regenerated)
priv/generated/driver_tab_android.zig # dispatch table (regenerated)
```
Plus, depending on `--type`:
```
c_src/<name>.c # --type c
native/<name>/Cargo.toml # --type rustler
native/<name>/src/lib.rs # --type rustler
native/<name>/.cargo/config.toml # --type rustler (macOS link flags)
```
For `mob.enable <feature>` the file list varies per feature — see
the individual feature docs via `mix help mob.enable`.
## Where to dig deeper
| Topic | Location |
|---|---|
| Per-backend mechanics, how each upstream library works, what Mob changes, transient workarounds | [`mob_dev/guides/nifs.md`](https://hexdocs.pm/mob_dev/nifs.html) |
| Embedded CPython app integration (wheels, first-launch extraction, host-dev fallback) | [`mob_dev/guides/python_embedding.md`](https://hexdocs.pm/mob_dev/python_embedding.html) |
| `MobDev.StaticNifs` schema (arch values, per-arch symbol naming) | `MobDev.StaticNifs` module doc |
| Full task references | `mix help mob.add_nif`, `mix help mob.enable` |