Appearance
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.jslogs include MMKV and TurboModule patches
- [ ] Confirm React versions are aligned (must match RN renderer):
react,react-dom, andreact-test-rendererare pinned consistently in rootpackage.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 -Obytecode generation is skipped
- Hermes remains the runtime; only unstable
- [ ] Confirm CocoaPods install succeeds:
cd apps/mobile/ios && pod install
- [ ] Confirm release env values are set correctly:
apps/mobile/.envand 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
- Open
apps/mobile/ios/Touli.xcworkspacein Xcode. - Select
Any iOS Device (arm64)or generic iOS destination. - Product -> Clean Build Folder.
- Build in
Releaseconfiguration once to catch compile errors early. - Archive.
- Validate archive.
- 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:
- Pull
.ipscrash logs from device/TestFlight. - Check build number and app UUID match the uploaded archive.
- Classify crash type:
- compile-time/toolchain
- native exception
- JS fatal exception surfaced through native
- Symbolicate against matching archive/dSYM.
- Capture device console logs for the exact JS fatal message if stacks are generic.
- Patch minimally, rebuild, increment build number, re-test.
Known gotchas observed in this project
- Do not run
yarn installinsideapps/mobile/. That creates a nestedapps/mobile/node_modulesand can pull incompatible Expo package versions (for exampleexpo-notifications@57.xwhile 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 (
reactvsreact-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.