Skip to Content
MobileBuild & release

Build & release

Toolchain

ToolVersion / note
Node20.x
React Native0.78.2 (Hermes)
Package managernpm (package-lock.json) — a stray yarn.lock may exist; standardise on npm
iOSXcode + CocoaPods via Bundler (bundle exec pod install)
AndroidAndroid SDK + NDK 27.1.12297006 (r27b)

[!WARNING] Two recurring local-build traps:

  • Partial NDK install[CXX1101] NDK … did not have a source.properties file. Reinstall the exact version: sdkmanager "ndk;27.1.12297006".
  • Stale Pods after a node_modules change → missing React headers. Fix: rm -rf node_modules && npm install, then cd ios && pod install --repo-update.

Local development

npm install # also runs patch-package (postinstall) cd ios && bundle exec pod install && cd .. # iOS only npm start # Metro (port 8081) npm run ios # build + run on a simulator npm run android # build + run on an emulator/device

Fastlane

Release automation lives in fastlane/Fastfile. The main lanes:

LaneWhat it does
ios build / android build_apk/buildBuild only, no upload
ios beta / android betaBuild + upload to TestFlight / Play internal
ios release / android releaseBump patch version, build, submit to App Store review / Play production, tag + push
bump_build / bump_patchVersion bumps (also exposed as npm run version:*)

[!NOTE] The release lanes run bump_patch first (commit + tag + push), so don’t run them without all credentials present — a failure mid-lane still leaves a version bump committed. Production deploys are intended to run from CI.

Code signing

  • iOS — App Store Connect API key (.p8), via env: APP_STORE_CONNECT_API_KEY_KEY_ID, _ISSUER_ID, _KEY_FILEPATH. No interactive Apple 2FA needed.
  • Android — release keystore android/app/release-keystore.iba.jks, unlocked by ANDROID_APP_PRIVATE_KEY, ANDROID_APP_ALIAS, and the store/key passwords.

These secrets are not committed — they live in the build environment (CI secrets, or fastlane/.env locally). See Configuration.

Versioning

package.json version is the source of truth, synced to the native projects by scripts/version.js (npm run version:status / version:build / version:patch). iOS CURRENT_PROJECT_VERSION and Android versionCode are the build numbers.

Source maps

So production Sentry stack traces show real file/function names (not minified ?anon_0_ frames), source maps are uploaded on release builds:

  • Metro (metro.config.js) is wrapped with withSentryConfig, which injects debug IDs into the bundle + source map so they match the exact build.
  • iOS uploads during the Xcode “Bundle React Native” phase (sentry-xcode.sh); Android via the @sentry/react-native sentry.gradle integration.
  • Both authenticate with a Sentry organization token (org:ci scope) read from SENTRY_AUTH_TOKEN in the build environment. ios/sentry.properties and android/sentry.properties hold the org/project only — never the token.

Without SENTRY_AUTH_TOKEN the build still succeeds but silently skips the upload. Verify after a release that the new version shows uploaded artifacts in Sentry and that fresh issues are symbolicated.

Last updated on