Skip to content

Make the iOS hot-reload port configurable - #357

Open
shanerbaner82 wants to merge 2 commits into
mainfrom
feat/configurable-hot-reload-port
Open

Make the iOS hot-reload port configurable#357
shanerbaner82 wants to merge 2 commits into
mainfrom
feat/configurable-hot-reload-port

Conversation

@shanerbaner82

Copy link
Copy Markdown
Contributor

Why

The in-app hot-reload server bound a hardcoded 9999. That port is host-wide — a simulator shares the host's localhost, and physical devices are tunnelled to the same host port by iproxy — so only one app could hot-reload at a time.

Worse, the collision is silent. When a second app (or a stale instance of the same one) already holds the port, triggerIosReload()'s fsockopen connects successfully and reports nothing wrong. The trigger lands on the wrong listener, so files sync, watchman reports success, and the live app never reloads.

What

Adds nativephp.hot_reload.port, defaulting to 9999 — no behaviour change for existing apps.

  • HasHotReloadPort — one hotReloadPort() accessor, used by WatchesIos, RunsIos, and BuildIosAppCommand
  • WatchesIos — reload trigger, iproxy forward ({port}:{port}), both lsof cleanups, and the failure message
  • RunsIos — the pre-launch port free
  • BuildIosAppCommand — writes NATIVEPHP_HOT_RELOAD_PORT into Info.plist, alongside the existing BIFROST_APP_ID handling
  • HotReloadServer.swift — reads that key from Bundle.main, falling back to 9999

Swift can't read the Laravel config, so the port has to be baked into Info.plist at build time and read back at launch. Changing the port therefore needs a rebuild — noted in the config comment.

iOS only. Android signals a reload by pushing a file into the app's storage over adb and never binds a port, so it ignores the key.

Tests

tests/Feature/IosHotReloadPortTest.php, modelled on IosInterfaceStyleTest: the configured port is written, an unset port falls back to the default, and an existing key is updated rather than duplicated.

The fallback test caught a real bug during development — config() only applies its default when the key is absent, so an app that published the key but left it empty yielded port 0. hotReloadPort() now coerces empty to the default.

🤖 Generated with Claude Code

shanerbaner82 and others added 2 commits August 18, 2026 15:48
The in-app hot-reload server bound a hardcoded 9999. That port is host-wide
— a simulator shares the host's localhost, and physical devices are tunnelled
to the same host port by iproxy — so only one app could hot-reload at a time.
A second app (or a stale instance of the same one) silently answered the
reload trigger instead, leaving the live app synced but never reloaded.

Adds `nativephp.hot_reload.port`, defaulting to 9999 so nothing changes for
existing apps. Swift can't read the Laravel config, so the value is written
into Info.plist at build time and read back from the bundle at launch, with
the same 9999 fallback for apps built before the key existed.

iOS only: Android signals a reload by pushing a file into the app's storage
over adb and never binds a port.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
config() does not treat an empty published key as missing, so these cases would otherwise bake port 0 into Info.plist.
Sign up for free to 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.

1 participant