ReadyNative

No ios/ or android/ folders

How ReadyNative keeps native projects generated instead of committed - where native config lives, how to add a native library, how SDK upgrades stay a version bump, and how that pairs with over-the-air updates.

Hey - open the repo and you won't find ios/ or android/. That's on purpose. ReadyNative uses Expo's Continuous Native Generation (CNG): the Xcode and Gradle projects are build output, generated from your config every time you build, the same way dist/ is generated from src/.

Why it matters

  • Upgrades are a version bump. Moving to the next Expo SDK is bunx expo install expo@latest --fix and a rebuild - there's no hand-merged Podfile, AppDelegate or build.gradle to reconcile.
  • Native config is reviewable. Bundle ids, permissions, entitlements and plugins are a few lines of TypeScript in a pull request, not a 3,000-line project.pbxproj diff.
  • Modules can't drift. Picking --widgets expo-widgets adds a widget extension target, an App Group and NSSupportsLiveActivities; picking none removes them. Nobody edits Xcode by hand, so nothing is left behind.
  • No Mac required. EAS generates and builds the native projects in the cloud.

Where native config lives

WhatWhere
App name, bundle id, scheme, brand colours, store idsreadynative.config.ts - the one file you edit
Everything else in the Expo configapp.config.ts, which reads readynative.config.ts
What each module needs (plugins, permissions, privacy)the module's app patch, merged by setup into .readynative.json and applied by app.config.ts
Anything the Expo config can't expressa config plugin in plugins/ (e.g. with-scene-lifecycle.js)
Apple privacy manifestcomposed from every selected module's privacy block - see Privacy

Want to see the result? Generate the projects locally and look - they're gitignored:

bunx expo prebuild --clean

bun run ios / bun run android (and every eas build) do that for you before compiling.

Adding a native library

  1. Install it with bunx expo install <package> so you get the version that matches the SDK.
  2. If it ships a config plugin, add it to plugins in app.config.ts with its options.
  3. Rebuild the dev build (bun run ios) - JavaScript reloads can't add native code.

That's the whole flow; there's no pod install to run or Xcode setting to flip.

Pairs with over-the-air updates

Because the native layer is generated from config, it's clear what needs a store release and what doesn't. runtimeVersion follows the app version (policy: "appVersion"), and every EAS build profile publishes to its own channel (development, preview, production in eas.json), so:

  • JavaScript, assets, translations, screens - ship with eas update; installed apps show the built-in Update ready banner and restart into it. Your first update walks through it.
  • Anything in the table above, or a new native library - bump the version and ship a store build; an update can never reach a binary it doesn't match.

When you'd commit native folders

Rarely. If you must hand-edit native code that no config plugin covers, you can run bunx expo prebuild, commit ios/ and android/, and maintain them yourself from then on - but you give up regenerated upgrades and module switching. Writing a small config plugin in plugins/ is almost always the better trade.

On this page

Get ReadyNative