Search documentation of mob

# Native Extensions

Copy Markdown View Source

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`. 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.

    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. 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)

    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.

## 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`
Embedded CPython app integration (wheels, first-launch extraction, host-dev fallback) | `mob_dev/guides/python_embedding.md`
`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`

← Previous Page Permissions
Next Page → DNS on iOS