feat: Expo managed workflow (expo-dev-client) support for iOS#42
Draft
CAMOBAP wants to merge 2 commits into
Draft
feat: Expo managed workflow (expo-dev-client) support for iOS#42CAMOBAP wants to merge 2 commits into
CAMOBAP wants to merge 2 commits into
Conversation
- Add runtime ObjC dispatch via objc_getClass/performSelector to derive the sandbox bundle URL from EXDevLauncherController.sourceUrl without a hard compile-time dependency on expo-dev-launcher headers - Add KVO on EXDevLauncherController.sourceUrl in SandboxReactNativeViewComponentView to retry sandbox load once the Expo host bundle URL becomes available (set asynchronously after deep-link network check, ~1-2s after launch) - Expose bundleURL as a public method on SandboxReactNativeDelegate - Add apps/expo-demo: a minimal Expo SDK 54 / RN 0.81.4 managed-workflow app that exercises the sandbox inside an expo-dev-client build - Remove orphaned apps/expo/ (abandoned WIP native build output, no package.json), replace with apps/expo-demo using managed workflow - Pin metro extraNodeModules.react to workspace root to avoid bun hoisting a stale react@19.0.0 into the app's local node_modules Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix stale comment referencing RCTJavaScriptDidLoadNotification (the implementation uses KVO, not the notification) - Forward unhandled KVO observations to super in observeValueForKeyPath:ofObject:change:context: - Remove duplicate #import <objc/runtime.h> in SandboxReactNativeDelegate.mm - Use a typed static context pointer (kExpoSourceUrlKVOContext) for KVO registration/removal instead of nil, preventing accidental dispatch of superclass observations into our handler - Correct infiniteLoop comment: hangs sandbox JS thread only, host app stays responsive (each sandbox runs in its own Hermes instance) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for running
@callstack/react-native-sandboxinside an Expo managed workflow app that usesexpo-dev-client(tested on SDK 54 / RN 0.81.4).Problem
Expo dev-client does not configure
RCTBundleURLProvider, sojsBundleURLForBundleRoot:returns nil. Additionally,EXDevLauncherController.sourceUrl(the host app's Metro bundle URL) is set asynchronously ~1–2s after launch, after a deep-link network check completes — making it nil at the time the sandbox view first tries to load.Solution
objc_getClass/performSelector:) to accessEXDevLauncherControllerwithout a hard compile-time dependency onexpo-dev-launcherheaders (which pull in Swift-generated headers that fail to compile from a library pod)EXDevLauncherController.sourceUrlto retry sandbox load once the URL becomes available, instead of polling or relying onRCTJavaScriptDidLoadNotification(which doesn't fire in New Architecture / bridgeless mode)sourceUrlcontains transform params (transform.routerRoot,transform.bytecode, etc.) that break Metro when applied to the sandbox bundle; only standard params (platform=ios&dev=true&hot=false) are usedChanges
Library (
packages/react-native-sandbox/ios/)SandboxReactNativeDelegate.mm— Expo URL derivation inbundleURLvia runtime ObjC dispatchSandboxReactNativeDelegate.h— exposebundleURLas a public methodSandboxReactNativeViewComponentView.mm— KVO observer onsourceUrlwith proper lifecycle (cleanup inprepareForRecycleanddealloc), typed KVO context pointerDemo app (
apps/expo-demo/)expo-dev-clientapps/expo/directory (abandoned WIP with nopackage.json)reactto workspace root to prevent bun from hoisting a stale transitive versionWhat's not included (follow-up)
expo-demoTest plan
cd apps/expo-demo && npx expo run:ios(requiresexpo-dev-clientbuild)exp+expo-demo://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8082onError) fires correctly for sandbox errors🤖 Generated with Claude Code