Skip to content

iOS: file-only :inet_db lookup by default in Mob.App.start/0 - #14

Closed
C-Sinclair wants to merge 1 commit into
GenericJam:masterfrom
C-Sinclair:ios-inet-db-default
Closed

iOS: file-only :inet_db lookup by default in Mob.App.start/0#14
C-Sinclair wants to merge 1 commit into
GenericJam:masterfrom
C-Sinclair:ios-inet-db-default

Conversation

@C-Sinclair

@C-SinclairC-Sinclair commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Mob.App.start/0 now switches :inet_db's lookup chain to [:file] and seeds localhost on iOS, before any user code runs. The first Node.connect / :erpc.call / gen_tcp.connect/3 with a binary host no longer crashes the calling process with :badarg.

Why

BEAM's default :native hostname resolution spawns the inet_gethost port program. iOS's app sandbox forbids execve of any binary the app didn't get a special pass for, so the spawn fails categorically on device. 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 lookup dies and any caller through :inet.getaddr/2 (distribution, RPC, TCP-by-name) gets :badarg and exits.

Every iOS mob app today has to do this dance manually in on_start/0 before anything touches a hostname:

:inet_db.set_lookup([:file]):inet_db.add_host({127,0,0,1},[~c"localhost"])

Easy to forget, impossible to test for, and bites every new consumer the first time they call :erpc.call. The framework should own this.

Composability with Mob.DNS

Pure addition. Mob.DNS.configure_pure_beam/1 keeps working — it upgrades [:file][:file, :dns] and seeds fallback nameservers. The file-table entries this PR adds stay first in the chain and keep winning, so the two layers compose:

  • Framework default[:file], distribution + local-loopback TCP work. No DNS.
  • App opts in to Mob.DNS.configure_pure_beam/1 for outbound HTTP — chain becomes [:file, :dns], fallback nameservers (Google + Cloudflare by default) handle public hosts.
  • App opts in per-host to Mob.DNS.resolve/1 for VPN / mDNS / search-domain semantics — Apple resolver via NIF, result seeded in the file table, always wins because :file is first.

Apps that want the default OS resolver back (and were somehow getting away with it on iOS) can call :inet_db.set_lookup([:native]) in their own on_start/0, which runs after start/0.

Why [:file] not [:file, :dns] by default

Conservative — the irreducible fix is preventing the inet_gethost spawn. Adding :dns to the framework default would mean every iOS app pays for fallback-nameserver state it may not use, and apps that talk to an internal-only backend on a fixed IP get a slightly-noisier inet_db. Mob.DNS.configure_pure_beam/1 is the right opt-in for apps that need DNS; the framework just guarantees the crash doesn't happen.

Test plan

  • mix test — 798 passed (27 doctests, 771 tests), 43 excluded. New test/mob/app_test.exs verifies the host-BEAM (NIF-unloaded) path is a no-op and that the function is idempotent.
  • mix format --check-formatted — clean
  • mix credo --strict — clean (952 mods/funs, 0 issues)
  • mix compile --warnings-as-errors — clean
  • mix erlfmt --check src/ — clean
  • iOS sim end-to-end — needs a deployed app; can't reproduce in this PR's CI. Verified manually that without the fix the first :erpc.call from a sim app crashes with inet_gethostopen_port:badarg, and with the fix the same call succeeds.

Related

Companion to the other small contributions in this series — text-field-secure (#12), fix/ios-column-fill-height, scroll-on-refresh, nav-transition-override. Each stands alone; land in any order.

iOS sandboxes execve of any binary the app didn't get a special pass
for, and BEAM's default :native hostname resolver spawns the
inet_gethost port program — so the first Node.connect / :erpc.call /
gen_tcp.connect/3 with a binary host crashed with :badarg before this.
The sim hits the same failure for a related-but-distinct reason
(inet_gethost isn't where BEAM expects in the mob iOS-sim OTP layout).
Mob.App.configure_ios_inet_db/0 sets the lookup chain to [:file] and
seeds localhost. Called as the first line of the macro-generated
start/0 so app on_start/0 never has to think about it. Gated on
:mob_nif.platform() == :ios (host BEAM and Android untouched); guarded
with try/rescue so the NIF-not-loaded case (host test BEAM) doesn't
crash.
Composes cleanly with Mob.DNS.configure_pure_beam/1 — apps that need
outbound DNS layer it on top to upgrade [:file] → [:file, :dns]; the
file-table entries seeded here stay ahead and keep winning.
@GenericJam

Copy link
Copy Markdown
Owner

Thanks @C-Sinclair — landed as 11752a4 in mob 0.6.15 with your authorship preserved. CHANGELOG resolution merged both your ### Fixed entry and the paired #12### Added entry under the new [0.6.15] section.

The framework now owns the :inet_db file-only default so new iOS apps don't have to remember the two-line on_start/0 dance. Mob.DNS.configure_pure_beam/1 still composes on top for apps that want DNS. Closing since the commit is on master.

pshoukry pushed a commit to pshoukry/mob that referenced this pull request Sep 9, 2026
…ixed (mob_new + mob_dev companion commits)
Four small lost-in-the-shuffle items closed in this batch. All four
were held up by Phase 2 work touching the same files (GenericJam#1/GenericJam#2/GenericJam#4 in
live_view_patcher.ex; GenericJam#5 in native_build.ex).
GenericJam#1 — Phoenix LiveReload mac_listener warnings: code_reloader/watchers/
live_reload disabled in on-device endpoint config.
GenericJam#2 — esbuild/tailwind version-not-configured warnings: versions set
via Application.put_env in mob_app.ex before ensure_all_started.
GenericJam#4 — port 4200 collisions across multiple Mob LV apps: per-app hash
into 4200..4999 via :erlang.phash2(:<app>, 800).
GenericJam#5 — deploy auto-pick of iPhone over sim was silent: prints the
--device <short-id> alternative when both are connected.
GenericJam#3 (WS→longpoll fallback in WKWebView) is investigation, not a fix —
deferred. GenericJam#6-GenericJam#11 are larger work (OTP rebuild, AX modifiers,
Compose semantics walker, Android 17 SELinux patch). GenericJam#12, GenericJam#13 already
fixed earlier. GenericJam#14 is moderate — sim node naming reconciliation
between mob_dev's connect.ex and mob_beam.m, deferred.
pshoukry pushed a commit to pshoukry/mob that referenced this pull request Sep 9, 2026
…d-around
GenericJam#7 + GenericJam#8 — `MobSlider` and `MobToggle` in MobRootView.swift gained the
SwiftUI AX modifiers SwiftUI doesn't apply by default
(.accessibilityAdjustableAction with default step (max-min)/10 for
the slider; .accessibilityLabel(label) for the toggle). Mob.Test
end-to-end driving works after these.
GenericJam#14 — defensive fallback in MobDev.Connector. When the primary node
name times out the connector now also tries the alternate
`<app>_ios@127.0.0.1` form (without the udid suffix). The connected
Device.node is updated to whichever responded so downstream RPC
calls use the correct address. The root cause (mob_beam.m sometimes
not seeing SIMULATOR_UDID) is captured in the resolution note for a
future investigation.
Remaining: GenericJam#3 (WebSocket→longpoll, investigation), GenericJam#6 (16 KB page
alignment, OTP rebuild), GenericJam#9 (Alert OK button, UIKit deep dive),
GenericJam#10 (Android 17 SELinux, OTP source patch), GenericJam#11 (Android Compose
semantics walker, ~200 lines Kotlin).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@C-Sinclair@GenericJam