Mob.App behaviour (mob v0.6.15)

Copy Markdown View Source

Behaviour for Mob application entry point.

Usage

defmodule MyApp do
  use Mob.App

  def navigation(_platform) do
    stack(:home, root: MyApp.HomeScreen)
  end

  def on_start do
    Mob.Screen.start_root(MyApp.HomeScreen)
    Mob.Dist.ensure_started(node: :"my_app@127.0.0.1", cookie: :secret)
  end
end

use Mob.App generates a start/0 that the BEAM entry point calls. It handles all framework initialization (native logger, navigation registry) before delegating to on_start/0. App code only goes in on_start/0.

Navigation

Implement navigation/1 to declare the app's navigation structure. Use the helper functions stack/2, tab_bar/1, and drawer/1:

def navigation(:ios),     do: tab_bar([stack(:home, root: HomeScreen), ...])
def navigation(:android), do: drawer([stack(:home, root: HomeScreen), ...])
def navigation(_),        do: stack(:home, root: HomeScreen)

All name atoms used in stacks become valid push_screen destinations without needing to reference modules directly.

Summary

Callbacks

App-specific startup hook. Called by the generated start/0 after all framework initialization is complete.

Functions

Apply the iOS-only :inet_db workaround so distribution, RPC, and TCP-by-hostname don't crash on the first lookup.

Declare a side drawer containing multiple named stacks.

Declare a navigation stack.

Declare a tab bar containing multiple named stacks.

Callbacks

navigation(platform)

@callback navigation(platform :: atom()) :: map()

on_start()

(optional)
@callback on_start() :: term()

App-specific startup hook. Called by the generated start/0 after all framework initialization is complete.

Override to start your root screen, configure Erlang distribution, set the Logger level, etc. The default implementation is a no-op.

Functions

configure_ios_inet_db()

@spec configure_ios_inet_db() :: :ok

Apply the iOS-only :inet_db workaround so distribution, RPC, and TCP-by-hostname don't crash on the first lookup.

iOS sandboxes any app that isn't Apple's own and refuses execve of binaries the app didn't get a special pass for. BEAM's default :native hostname-resolution path spawns the inet_gethost port program — exactly the kind of execve iOS rejects — so the very first :inet.getaddr/2 call (transitively reached by Node.connect, :erpc.call, gen_tcp.connect/3 with a binary host, etc.) crashes the calling process with :badarg. The simulator hits the same failure for a related but distinct reason: inet_gethost doesn't live at the path BEAM expects under the mob iOS sim OTP layout. Either way, the fix is the same.

Switching the lookup chain to [:file] keeps everything in BEAM's in-process name table — no port program, no fork, no execve. We also seed localhost so apps using @localhost node names (or any gen_tcp call that resolves "localhost") work without further setup.

Called automatically by the macro-generated start/0 before anything else, so app on_start/0 code never has to think about it. Apps that need outbound DNS (Req / Finch / Mint to arbitrary hosts) can layer Mob.DNS.configure_pure_beam/1 on top — it upgrades the chain to [:file, :dns] and seeds fallback nameservers, while the file-table entries we add here keep winning.

Other platforms (:android, :host) are unaffected — BEAM's native resolver works there. Safe to call on host BEAM where the NIF isn't loaded; rescues the UndefinedFunctionError / ErlangError and returns :ok.

drawer(branches)

@spec drawer([map()]) :: map()

Declare a side drawer containing multiple named stacks.

Renders as a ModalNavigationDrawer on Android. iOS uses a custom slide-in panel (native UIKit drawer support deferred).

stack(name, opts)

@spec stack(
  atom(),
  keyword()
) :: map()

Declare a navigation stack.

name is the atom identifier used with push_screen/2,3, pop_to/2, and reset_to/2,3. The :root option is the module mounted when the stack is first entered.

Options:

  • :root (required) — screen module that is the stack's initial screen
  • :title — optional display label shown in tabs or drawer entries

tab_bar(branches)

@spec tab_bar([map()]) :: map()

Declare a tab bar containing multiple named stacks.

Each branch must be a stack/2 map. Renders as a bottom NavigationBar on Android and a UITabBarController on iOS.