GenericJam

GenericJam

Proposed Plugin System for Mob - BEAM Mobile Framework

Plugin System for Mob

Mob is growing by leaps and bounds! I realize now we will not be able to accommodate all the functionality people would want in the main framework itself. It is designed to be extensible. The build system should be able to handle plugins now.

The idea for the plugins is similar to React Native plugins. This is more than coincidence because React Native and Mob have similar architectures, with Mob being a bit simpler. The native code in Kotlin/Java, Swift/ObjC, C is likely very close to what Mob would need. The main difference is the logic is in Elixir, not JS and the distribution is hex.

One of the upsides of the similarity is we could migrate many of these to Mob which would be a big boost to the ecosystem. There are ~150-200 that land in this ‘native heavy’ category that would be very attractive to use such as date pickers, etc. that translate to native elements.

So you’d have the Android native code in /android and the iOS native code in /ios. You could have one sided plugins that only land on one platform.

I’ve brainstormed a pretty extensive document on this with Claude. Here’s the shorter summary:

Mob Plugins: Manifest Schema Proposal

Three design choices anchor this proposal. First, the manifest is data, not code — mob_dev reads priv/mob_plugin.exs at compile time rather than having plugins call register_plugin at runtime. Static, inspectable, validatable; closer to mix.exs than to Phoenix’s runtime route registration. Second, activation is explicit and separate from installation, borrowed from how iOS entitlements work: a framework supporting capability X doesn’t mean your app uses X without explicit declaration. This mitigates supply-chain risk by ensuring mix deps.get can never silently modify your app’s permission set. Third, the schema scales with plugin tier rather than being exhaustive everywhere — small plugins write three fields, large plugins write a dozen sections, and the shape of the manifest doesn’t make trivial plugins look heavy. Underlying all of this: Hex is the substrate, so versioning, dep resolution, security posture, and hexdocs publication come free, and static linking is required (no dlopen) to stay App-Store-compatible.

The schema spans five tiers. Tier-0 is a pure Elixir Hex package needing no manifest at all. Tier-1 adds NIFs and per-platform native sources — Kotlin bridge files, Swift files, Gradle deps, frameworks, permissions, plist keys. Tier-2 introduces :ui_components for new render-tree node types with paired SwiftUI views and Composables. Tier-3 layers on :screens, :migrations, and :assets for plugins that ship entire mini-applications. Tier-4 adds :lifecycle, :settings, and :notifications for embedded sub-apps with supervised children, persisted user settings, and push notification handlers. Required top-level fields are just :name, :mob_version, and :plugin_spec_version; everything else is independently optional.

Installation is deliberately two-step. Adding a plugin to deps and running mix deps.get makes it resolvable but does not merge native code, permissions, or plist keys. Activation requires an explicit entry in config :mob, :plugins in mob.exs. mob_dev prints the diff at compile time so users see exactly what’s being added — permission set changes are never silent. mix mob.add_plugin wraps both steps plus optional interactive setup prompts where plugin authors guide tier-3/4 integration (e.g., “Register MobChatKit.MessageListScreen in your App.navigation/1?”). The standard flow always works; the convenience command is not a required entry point.

Validation runs at two stages and fails loud. mix mob.validate_plugin checks the plugin in isolation: paths exist, files parse, version requirements are valid, and single-platform UI components are flagged as warnings (the #1 React Native plugin pain point). mob_dev then re-validates at compile time across all activated plugins — no duplicate component atoms, no colliding screen routes, no migration namespace collisions, all activated plugins present in deps. Hot-pushability is computed automatically from which sections are populated: tier-0 hot-pushes fully, tiers 1-2 require a native rebuild, tiers 3-4 are partial (Elixir hot-pushes; native does not). Forward compatibility is handled via :plugin_spec_version, letting the schema evolve without breaking existing plugins — bump the integer, support both versions in mob_dev for a migration window, deprecate.

Any comments or questions are appreciated.

Where Next?

Popular in RFCs Top

laibulle
Hello, I am playing with quantitative finance with Elixir. This library is more a way for me to explore and learn in this area and especi...
New
dimamik
Hi! I’ve been working with Pipecat in Python for quite a while now, and while it’s effortless to set up, when building AI voice agents at...
New
WyvernoDragon
I’ve been working on pgRx: a query engine that uses BEAM lightweight processes instead of PostgreSQL’s fork-per-connection model. The nu...
New
Hedgehog-ai
Hello I would like feedback on an experimental neuroevolution (including substrate encoding) library called Bardo based on the amazing wo...
New
bluzky
Hi everyone, I would like to introduce my new project OrangeCMS, it’s an application that help you to create/edit content post for your ...
New
jarlah
Hi! I have recently created, after having tried to get in touch with the creator of excontainers for quite some time, a new library call...
New
sodapopcan
EDIT: I forgot to link the repo, ha: GitHub - sodapopcan/vials: Wrappers for mix tasks This project is not quite ready for prime-time bu...
New
manuel-rubio
There was some time when I started thinking about giving a boost to Lambdapad, the initiative from @garretsmith in Erlang that I loved wa...
New
KristerV
How I currently use Hexdocs I use hexdocs all day every day, but finding the right module and function takes too long even with my bookma...
New
BartOtten
This thread once discussed Routex in it’s early form. It has been repurposed to gather feedback and discusses pre-releases. Currently: p...
New

Other popular topics Top

mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I fore...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
hariharasudhan94
Lets say i have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => "XX...
New

We're in Beta

About us Mission Statement