Skip to content

iOS Release Runbook (Touli Mobile)

This runbook is for shipping apps/mobile to TestFlight with Xcode archives (without relying on EAS Build).

Scope and assumptions

  • App: apps/mobile
  • Workspace: apps/mobile/ios/Touli.xcworkspace
  • Bundle ID: com.touli.development (adjust for production)
  • Toolchain is pinned and known-good for the current release window

Golden rule

Do not change Xcode major version, Expo SDK, React Native version, and native dependency versions in the same release window unless absolutely necessary.

Preflight checklist

  • [ ] Confirm clean dependency install:
    • yarn install
  • [ ] Confirm postinstall patches applied:
    • scripts/fix-node-modules.js logs include MMKV and TurboModule patches
  • [ ] Confirm React versions are aligned (must match RN renderer):
    • react, react-dom, and react-test-renderer are pinned consistently in root package.json
  • [ ] Confirm Expo config includes required iOS release plugins/settings:
    • apps/mobile/app.config.ts
  • [ ] Confirm Release uses a source JS bundle (USE_HERMES = false):
    • Hermes remains the runtime; only unstable hermesc -O bytecode generation is skipped
  • [ ] Confirm CocoaPods install succeeds:
    • cd apps/mobile/ios && pod install
  • [ ] Confirm release env values are set correctly:
    • apps/mobile/.env and runtime API URL
  • [ ] Confirm signing setup in Xcode:
    • Team, bundle ID, provisioning profile, capabilities

Versioning rules

  • CFBundleShortVersionString (marketing version) can stay the same for hotfixes.
  • CFBundleVersion (build number) must increment for every TestFlight upload.
  • Never re-upload the same build number.

Build and archive flow

  1. Open apps/mobile/ios/Touli.xcworkspace in Xcode.
  2. Select Any iOS Device (arm64) or generic iOS destination.
  3. Product -> Clean Build Folder.
  4. Build in Release configuration once to catch compile errors early.
  5. Archive.
  6. Validate archive.
  7. Distribute App -> App Store Connect -> Upload.

TestFlight smoke test (required)

After processing completes, install the new TestFlight build on a physical device and verify:

  • [ ] App launches past splash screen.
  • [ ] Auth/session hydration works.
  • [ ] Network calls hit expected environment API.
  • [ ] Wallet/auth critical paths load without immediate crash.
  • [ ] No fatal JS exceptions in device logs on first launch.

Crash triage workflow

When a TestFlight crash happens, use this order:

  1. Pull .ips crash logs from device/TestFlight.
  2. Check build number and app UUID match the uploaded archive.
  3. Classify crash type:
    • compile-time/toolchain
    • native exception
    • JS fatal exception surfaced through native
  4. Symbolicate against matching archive/dSYM.
  5. Capture device console logs for the exact JS fatal message if stacks are generic.
  6. Patch minimally, rebuild, increment build number, re-test.

Known gotchas observed in this project

  • Do not run yarn install inside apps/mobile/. That creates a nested apps/mobile/node_modules and can pull incompatible Expo package versions (for example expo-notifications@57.x while the repo uses Expo SDK 54 / 0.32.x), which breaks CocoaPods linking.
  • React Native iOS TurboModule exception path can crash differently across iOS/Xcode versions.
  • Building RN from source can expose fmt/clang compatibility issues on newer Xcode.
  • React version mismatch (react vs react-native-renderer) can hard crash at startup.
  • Hermes 0.81.6 optimized bytecode can crash during runtime initialization on physical iOS 26 devices; Release currently embeds JavaScript source for Hermes to execute instead.
  • One fix can unmask the next layer; do not assume first visible crash is the root cause.

Stability practices for future releases

  • Keep a pinned release matrix in PR description:
    • Xcode version
    • iOS target version
    • Expo SDK
    • React Native
    • React/React DOM/react-test-renderer
  • Prefer small, isolated release PRs.
  • Cut a release-candidate build and run smoke tests before final submission.
  • Avoid dependency upgrades during urgent hotfix releases.

Suggested release PR checklist

  • [ ] Changelog/summary includes user-facing impact.
  • [ ] Build number bumped.
  • [ ] iOS archive succeeds locally.
  • [ ] TestFlight smoke test done on real device.
  • [ ] Crash logs reviewed if any startup instability appears.