Skip to main content

OTA split bundle — React Native runtime is not ready

Date: 2026-07-08

Problem

In OTA mode, remote pages failed to load with:

React Native runtime is not ready

Native rejected with NO_RUNTIME before the split bundle could execute.

Root cause

Two issues in SplitBundleLoader.mm (RN 0.86 bridgeless / RCTBridgeProxy):

  1. Runtime detectionisKindOfClass:[RCTBridgeProxy class] fails because RCTBridgeProxy subclasses NSProxy. NSProxy reports +class as NSProxy, so the kind check never matches and RuntimeForBridge returned nullptr.

  2. Promise timingresolve(nil) was called immediately after scheduling invokeAsync, before evaluateJavaScript finished. JS then called getFeatureComponent before registerFeature ran in the split bundle.

Solution

  • Native (SplitBundleLoader.mm): Stop using CallInvoker::invokeAsync + CurrentBridge() (runtime was null in that callback). Use RCTBridgeProxy's dispatchBlock:queue:RCTJSThread (dispatchToJSThread) to run on the JS runtime thread, capture the runtime pointer from the bridge at load time, and call evaluateJavaScript synchronously before resolving the promise.
  • JS (bundleLoader.ts): When OTA toggle is on, load cached file via native SplitBundleLoader before any Metro fallback.

Files changed

  • rn_app/ios/BrownfieldLib/SplitBundleLoader.mm
  • rn_app/src/features/bundleLoader.ts

Verification

  1. Rebuild brownfield package: npm run brownfield:package:ios:debug:simBUILD SUCCEEDED
  2. Run app from Xcode (or reinstall ios_native if it embeds the framework).
  3. Toggle OTA on a Remote page → check for updates → open Order/Promo.
  4. Page should load server bundle content without NO_RUNTIME error.

Notes

  • RCTBridgeProxy.runtime logs a migration warning; this is expected for split-bundle loading until RN exposes a public RuntimeExecutor API for third-party loaders.