Skip to content
RN/APPS

posts/precompiled-ios-and-hermes-bytecode.md · 2026-09-27

Precompiled iOS Builds and the Hermes Bytecode Check

Not financial advice. Verify claims independently.

React Native 0.81, the version Expo SDK 54 defaults to, changed the iOS build in a way finance apps feel immediately. React Native and its iOS dependencies ship as precompiled XCFrameworks alongside the source. Meta and Expo's measurement on RNTester was a clean build falling from about 120 seconds to about 10 seconds on an M4 Max. You should not budget a 10x win for a production app. Most of a trading client is still your own pods, chart libraries, and push SDKs, and those still compile. You should budget a real win anywhere React Native itself was the long pole, which is common on smaller quote apps and on tvOS targets that picked up the same precompiled frameworks.

The feature is still described as experimental in the 0.81 notes, with three caveats that show up in release week rather than in a blog skimming.

What precompiled builds refuse to do

You cannot step into React Native internals in a precompiled build. Your own native modules still debug. The framework is a binary. That is fine until a crash lands inside the renderer and the stack is a blur. Keep one from-source profile for the week you are chasing a Fabric bug. Do not make that profile the one you ship.

Xcode 26 betas, when they were current around the 0.81 release, built targets with Swift explicit modules and broke the precompiled path. The documented escape was setting SWIFT_ENABLE_EXPLICIT_MODULES to NO until a patch. If an iOS 26 SDK bump suddenly rebuilds the world, check that flag before you blame CocoaPods.

Using use_frameworks! forces React Native to build from source. Dynamic frameworks and the precompiled slice do not mix. If a vendor pod demands use_frameworks, you have chosen the slow path on purpose. Write that down in the EAS profile so nobody "fixes" CI by deleting the prebuild and doubling every iOS queue.

Android is the other half of the 0.81 headline and it is easy to skip: the release adds Android 16, API level 36. A quote app that targets the Play Store's moving API floor should bump the compile SDK in the same train as the iOS prebuild, not a quarter later when a store warning arrives.

The JSC removal is a Hermes story

React Native 0.81 also removes the in-tree JavaScriptCore. Support had already moved to a community package. Apps that still opt out of Hermes must take that package to upgrade. Apps on Hermes, which is the default, are unaffected. We treat a request to switch engines for a single screen as a bug. Two engines means two crash symbolication paths and a startup number you can no longer compare across releases.

Hermes pays off when the release build compiles JavaScript to bytecode. The .hbc artifact is the product. A development bundle that shows HermesInternal in the console is not evidence that production startup is using it. Teams get this wrong in two ways. They load a raw bundle through a custom OTA client and skip the bytecode file. Or they compare a debug build on a simulator against a release build on a phone and conclude Hermes did nothing.

The check is dull. Produce a release binary. Confirm the pipeline emits bytecode. Measure cold start to first watchlist paint on the same physical device before and after. React Native's own Hermes guide says the same thing, because the dev server hides the cost you are trying to remove.

Prebuilds and the native fingerprint

Precompiled frameworks are still a native artifact. An EAS Update channel does not care that your JavaScript did not change if one CI profile links the XCFramework and another compiles from source. The embedded binary and the update disagree, and the app refuses the OTA or, worse, applies a bundle over a different native tree. We keep one iOS build setting per channel. Preview and production either both use the precompiled slice or both do not. Mixing them to save CI minutes on pull requests is how a Thursday hotfix dies on TestFlight.

This is adjacent to the fingerprint config problem, but it is not the same bug. Ignoring lockfile noise will not save you if the binary underneath the fingerprint was produced two different ways. Record the Xcode version in the release notes for the binary. Precompiled artifacts are sensitive to the toolchain that built them, and a quiet Xcode bump on the EAS image is a native change even when your lockfile is identical.

A release gate that fits on one card

Before we call an SDK 54 cut done:

  • Clean iOS build time recorded, precompiled path actually on.
  • Release install, not a dev client, opens to a painted watchlist on a physical phone.
  • Hermes bytecode confirmed in that binary.
  • One architecture only. No Legacy fallback hiding in a flavor.
  • OTA applied onto a binary built with the same precompiled setting.

Then we open the same flow a customer opens. Stock Picks is the reference client we compare against: paper tickets, a live-feeling quote flash, and the startup path we are willing to put a number on. If your build is faster in CI and slower to first paint, you optimized the wrong clock. Precompiled iOS is a gift to the queue. Bytecode and a measured cold start are the gift to the person holding the phone.

cta/stockspicks.sh

Put it into practice

Rehearse on Stock Picks — the Expo fintech app from the team behind RN/APPS.

$ open stockspicks →