Skip to content

Commit 55c951c

Browse files
vzaidmanmeta-codesync[bot]
authored andcommitted
Add docs for the Metro connection on Apple platforms (#59035)
Summary: Pull Request resolved: #59035 Adds technical documentation for how a React Native app on Apple platforms connects to Metro, under `packages/react-native/React/DevSupport/__docs__/`. `README.md` gives the overview: the HTTP and WebSocket channels the app opens to Metro, a diagram of what happens at startup, and an endpoint reference. It links to one short document per topic: - how the bundle URL is chosen and how it can change at runtime - downloading and running the bundle - reloading and switching to a different Metro - the `/message`, `/hot` and `/inspector/device` connections - the JavaScript dev tools that talk to Metro (symbolication, open in editor, assets) - what each build mode and compile flag enables The docs follow the `__docs__` guidelines and are linked from the root documentation index. Changelog: [Internal] Reviewed By: christophpurrer Differential Revision: D124966085 fbshipit-source-id: d8dae624078c4ef78963fef548b353d13941cb05
1 parent 861635c commit 55c951c

10 files changed

Lines changed: 712 additions & 0 deletions

File tree

‎__docs__/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ TODO: Explain the different components of React Native at a high level.
7878
- UI
7979
- Events
8080
- Mounting
81+
- [Bundler Connection](../packages/react-native/React/DevSupport/__docs__/README.md)
8182
- Build system
8283
- Android
8384
- iOS
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Build Modes
2+
3+
[🏠 Home](../../../../../__docs__/README.md)
4+
5+
Part of [Bundler Connection (Apple platforms)](./README.md). What each build
6+
configuration and compile flag enables.
7+
8+
| Build | Flags | JavaScript comes from | Dev Menu, reload, Fast Refresh, DevTools |
9+
| ---------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------- |
10+
| Debug, simulator | `RCT_DEV=1`, `RCT_DEV_MENU=1` (from `DEBUG`) | Metro on `localhost`. No embedded bundle unless `FORCE_BUNDLING`. | Yes |
11+
| Debug, device | `RCT_DEV=1`, `RCT_DEV_MENU=1` (from `DEBUG`) | Metro at the development machine's IP; the embedded development bundle without it. | Yes |
12+
| Release | `RCT_DEV=0`, `RCT_DEV_MENU=0` | The embedded production bundle (Hermes bytecode with Hermes). | No |
13+
| Profiling | `RCT_DEV=0`, `REACT_NATIVE_DEBUGGER_MODE_PROD` (→ `RCT_REMOTE_PROFILE=1`, `RCT_ENABLE_INSPECTOR=1`) | The embedded production bundle. | React Native DevTools only |
14+
15+
`RCT_DEV` and `RCT_DEV_MENU` are separate flags so that a build can enable the
16+
Dev Menu independently of the rest of the development tooling. By default
17+
`RCT_DEV_MENU` follows `RCT_DEV`, which follows `DEBUG`; only the builds above
18+
have names. Other combinations are custom builds that define the macros
19+
explicitly:
20+
21+
| Combination | Flags | Result |
22+
| ------------------------------------ | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23+
| Dev Menu without development tooling | `RCT_DEV=0`, `RCT_DEV_MENU=1` | Dev Menu with Reload and Configure Bundler, and the loading banner. Metro is found only through an explicit `RCT_jsLocation`, checked with `/status`. Fast Refresh works when the bundle comes from Metro, because the `/hot` connection is opened from JavaScript and `RCT_DEV` does not gate it. No `/message` connection, DevTools or Cmd+R. |
24+
| Development tooling without Dev Menu | `RCT_DEV=1`, `RCT_DEV_MENU=0`, `RCT_PACKAGER_LOADING_FUNCTIONALITY=1` | Metro loading with host guessing, the `/message` connection, Fast Refresh and DevTools, without the Dev Menu or loading banner. Fast Refresh stays in its saved state (on by default), since the Dev Menu toggle is unavailable. A saved `RCT_jsLocation` is used without checking. `RCT_PACKAGER_LOADING_FUNCTIONALITY` keeps the `/status` checks that host guessing relies on. |
25+
26+
`scripts/react-native-xcode.sh` produces this during the Xcode build:
27+
28+
- Debug, device: writes `ip.txt` (unless `SKIP_BUNDLING_METRO_IP`) and embeds a
29+
development bundle (`--dev true`).
30+
- Debug, simulator: embeds nothing unless `FORCE_BUNDLING` is set.
31+
- Release: embeds a production bundle (`--dev false`).
32+
- `SKIP_BUNDLING` skips bundling in any configuration. `ENTRY_FILE`,
33+
`BUNDLE_NAME`, `BUNDLE_CONFIG` and `BUNDLE_COMMAND` customize the bundle
34+
command.
35+
36+
> **Example: RNTester "Build JS Bundle" phase**
37+
> (`packages/rn-tester/RNTesterPods.xcodeproj`)
38+
>
39+
> ```sh
40+
> export PROJECT_ROOT="$SRCROOT"
41+
> export ENTRY_FILE="$SRCROOT/js/RNTesterApp.ios.js"
42+
> export SOURCEMAP_FILE=../sourcemap.ios.map
43+
> # export FORCE_BUNDLING=true
44+
>
45+
> WITH_ENVIRONMENT="../react-native/scripts/xcode/with-environment.sh"
46+
> REACT_NATIVE_XCODE="../react-native/scripts/react-native-xcode.sh"
47+
>
48+
> /bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE"
49+
> ```
50+
51+
> **Example: HelloWorld build command** (`private/helloworld/cli.flow.js`)
52+
>
53+
> `build ios` passes `SKIP_BUNDLING: 'true'` to `xcodebuild`, so
54+
> `react-native-xcode.sh` does not bundle. A separate `bundle ios` command
55+
> writes `main.jsbundle` into the built app's resources.
56+
57+
In the native code, the behavior is controlled by flags in
58+
`React/Base/RCTDefines.h`:
59+
60+
- `RCT_DEV`: `DEBUG`.
61+
- `RCT_DEV_MENU`: `RCT_DEV`.
62+
- `RCT_REMOTE_PROFILE`: `REACT_NATIVE_DEBUGGER_MODE_PROD` if defined (profiling
63+
builds), otherwise `RCT_DEV`.
64+
- `RCT_ENABLE_INSPECTOR`: `RCT_DEV || RCT_REMOTE_PROFILE`.
65+
- `RCT_PACKAGER_LOADING_FUNCTIONALITY`: opt-in, undefined by default.
66+
67+
| Behavior | Compiled in when |
68+
| ---------------------------------------------------------------------- | ------------------------------------------------------ |
69+
| `/status` checks | `RCT_DEV_MENU` or `RCT_PACKAGER_LOADING_FUNCTIONALITY` |
70+
| Dropping a saved `RCT_jsLocation` where Metro does not answer | `RCT_DEV_MENU` |
71+
| Finding Metro through `ip.txt` or `localhost` | `RCT_DEV` |
72+
| `/message` connection, Cmd+R | `RCT_DEV` |
73+
| `RCTDevSettings` (Fast Refresh setup, Metro commands); stubs otherwise | `RCT_DEV_MENU` or `RCT_REMOTE_PROFILE` |
74+
| Dev Menu, `devMenu` command, loading banner | `RCT_DEV_MENU` |
75+
| DevTools connection, synchronous loading of plain-text file bundles | `RCT_ENABLE_INSPECTOR` |
76+
77+
Without these flags, `packagerServerHostPort` still returns an explicitly set
78+
`RCT_jsLocation`, unchecked. Apps normally do not call `RCTBundleURLProvider` in
79+
Release, as in the HelloWorld example in
80+
[Bundle URL](./BundleURL.md#choosing-the-bundle).
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Downloading and Running the Bundle
2+
3+
[🏠 Home](../../../../../__docs__/README.md)
4+
5+
Part of [Bundler Connection (Apple platforms)](./README.md). How the bundle is
6+
fetched from Metro or read from the app, and what happens when it loads or
7+
fails.
8+
9+
While loading, the app shows a "Loading from Metro…" banner at the top of the
10+
screen, followed by Metro's bundling progress and then the download progress. If
11+
loading fails, a red error screen (RedBox) shows the reason. When loading from a
12+
file in a Debug build, the banner says "Connect to Metro to develop JavaScript."
13+
14+
`-[RCTHost start]` takes the URL from the app's provider and creates an
15+
`RCTInstance` (`RCTInstance.mm`, next to `RCTHost.mm`). Once the JavaScript
16+
runtime is ready, `RCTInstance` calls `_loadJSBundle:` with
17+
`bundleManager.bundleURL`:
18+
19+
1. Creates the `DevSettings` module first. That opens the `/message` connection,
20+
so the `r` command in the Metro terminal still reloads the app when the
21+
download fails.
22+
2. Shows the banner (`RCTDevLoadingView`, `React/CoreModules/`).
23+
3. Calls `-[RCTHost loadBundleAtURL:…]`, which uses the host delegate's loader
24+
if it has one, otherwise `RCTJavaScriptLoader` (`React/Base/`).
25+
4. On error: posts `RCTJavaScriptDidFailToLoadNotification`, shows the RedBox,
26+
and calls `RCTFatal`.
27+
5. On success: runs the bundle, posts `RCTInstanceDidLoadBundle` and
28+
`RCTJavaScriptDidLoadNotification`, and connects Fast Refresh.
29+
30+
`RCTJavaScriptLoader` handles the two kinds of URL:
31+
32+
- File (embedded bundle): loaded synchronously when `RCT_ENABLE_INSPECTOR` is
33+
set, otherwise on a background queue.
34+
- Metro: `RCTMultipartDataTask` asks for a streamed response
35+
(`Accept: multipart/mixed`). Metro first sends JSON progress parts (`status`,
36+
`done`, `total`) for the banner, then the bundle itself. Download progress is
37+
reported for `application/javascript` and
38+
`application/x-metro-bytecode-bundle` parts. For the bundle part:
39+
- `X-Http-Status` overrides the HTTP status. A non-200 status becomes an error
40+
built from the response body, which is how Metro's build errors (such as a
41+
syntax error) reach the RedBox.
42+
- `Content-Type` must be `application/javascript` or `text/javascript`. For a
43+
JSON body, its `message` becomes the error.
44+
- `Content-Location`, if present, becomes the source URL used in stack traces.
45+
- The bundle is saved to `NSTemporaryDirectory()/ReactNativeDevBundle.js` and
46+
memory-mapped. The file URL is stored as
47+
`bundleManager.downloadedBundleFileURL`.
48+
- Network errors become "Could not connect to development server", with the
49+
URL.
50+
51+
If Metro does not answer with a multipart response, the bundle is downloaded
52+
as a single body.

0 commit comments

Comments
 (0)