Skip to content

Native changes

Every edit to android/ or ios/ beyond the React Native community template (0.87-stable) is listed here. Two reasons: upgrades (re-apply these after the Upgrade Helper diff) and an Expo version of the template (“Track B”, ADR 0001), where each row becomes an Expo config plugin. Search the code for [bareship] to find each change in place.

Area File(s) Change Why Expo (Track B) equivalent
Android flavors android/app/build.gradle environment flavor dimension: dev (.dev), staging (.staging), prod. Per-flavor app_name via resValue. debuggableVariants lists the flavored debug variants. Side-by-side installs per environment (ADR 0004) app.config.ts per APP_ENV, or a flavors config plugin
Android env values android/app/build.gradle Each flavor reads its own .env.<flavor> and adds every key as a buildConfigField + resValue (applyEnvFile). react-native-config’s dotenv.gradle is not applied. buildConfig = true; build_config_package resValue dotenv.gradle chooses one .env per Gradle run from the first task name, so ./gradlew assembleRelease or assembleDevDebug assembleProdRelease baked dev values into the prod app (found 2026-10-10). CI checks each build with scripts/ci/check-env.sh app.config.ts + expo-constants
Android strings android/app/src/main/res/values/strings.xml app_name removed (comes from the flavor) Avoids a duplicate resource n/a
Android release android/app/build.gradle, proguard-rules.pro R8 minifyEnabled + shrinkResources on; keep rule for BuildConfig; release signing from RN_UPLOAD_* properties or env vars, debug-key fallback with a warning Smaller release builds; no keys in git EAS credentials
Android icons android/app/src/<flavor>/res/mipmap-* Generated by pnpm icons; main has none Badge per environment icon per environment in app.config.ts
Splash (Android) MainActivity.kt, res/values/styles.xml, AndroidManifest.xml, res/drawable*/bootsplash_logo.png, res/values/colors.xml RNBootSplash.init(this, R.style.BootTheme) before super.onCreate; BootTheme set on the activity (generated by pnpm splash) Native splash until JS is ready expo-splash-screen
Splash (iOS) AppDelegate.swift, BootSplash.storyboard, Colors.xcassets, Images.xcassets/BootSplashLogo-*, Info.plist customize(_ rootView:) calls RNBootSplash.initWithStoryboard; UILaunchStoryboardName = BootSplash; template LaunchScreen.storyboard removed Same expo-splash-screen
iOS environments ios/Config/*.xcconfig, project.pbxproj, xcshareddata/xcschemes/* Six configurations (Debug/Release × Dev/Staging/Prod), each based on an xcconfig that includes the Pods xcconfig; schemes <App>-Dev/-Staging/-Prod; template Debug/Release and single scheme removed. Generated by scripts/ios/configure-environments.rb Per-environment bundle id, display name and icon (ADR 0004) app.config.ts per APP_ENV
iOS scene life cycle AppDelegate.swift, SceneDelegate.swift (new), Info.plist (UIApplicationSceneManifest), project.pbxproj The window and React Native start in a UIWindowSceneDelegate; AppDelegate only returns true. Cold-start URLs from connectionOptions are passed as launch options; runtime URLs and universal links are forwarded to RCTLinkingManager. The bootsplash hook moved here. Apps built with the iOS 27 SDK (Xcode 27) are killed at launch without the UIScene life cycle (“UIScene life cycle is required for apps built with this SDK”). The RN 0.87 template still uses the app-delegate window; RN’s main template has a SceneDelegate, backported here to 0.87 APIs Handled by Expo’s app delegate
iOS Swift DEBUG flag ios/Config/Debug-*.xcconfig, ios/Podfile SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG on the app’s and the pods’ Debug-* configurations RN only sets it on a configuration named exactly Debug; without it #if DEBUG is false in Debug-Dev and the app looks for an embedded bundle instead of Metro (“No script URL provided”) n/a
iOS Info.plist ios/<App>/Info.plist CFBundleDisplayName = $(APP_DISPLAY_NAME) Name comes from the environment xcconfig name in app.config.ts
iOS icons Images.xcassets/AppIcon{,-Dev,-Staging}.appiconset Single-size 1024 px icon per environment, chosen by ASSETCATALOG_COMPILER_APPICON_NAME Badge per environment icon per environment
iOS Podfile ios/Podfile project maps the six configurations to debug/release; post_install sets ENVFILE on the react-native-config pod per configuration CocoaPods and react-native-config understand the custom configurations n/a (managed by prebuild)
Area File(s) Change Why Expo (Track B) equivalent
Android NDK android/build.gradle subprojects { … plugins.withId("com.android.library") { android.ndkVersion = rootProject.ext.ndkVersion } } react-native-unistyles doesn’t read ndkVersion, so AGP 9 would install its default NDK (28.2) next to RN’s 27.1 Managed by prebuild
Screens fragments MainActivity.kt supportFragmentManager.fragmentFactory = RNScreensFragmentFactory() before RNBootSplash.init Required by react-native-screens ≥ 4.16 so restored fragments don’t crash, in the order react-native-bootsplash documents Handled by Expo
iOS pod deployment targets ios/Podfile (post_install) Raises every pod’s IPHONEOS_DEPLOYMENT_TARGET below min_ios_version_supported Xcode 27 rejects targets below iOS 15; SDWebImage (FastImage), Lottie, react-native-svg and Firebase helper bundles still declare 9.0–13.0 Handled by expo-build-properties

Firebase, push and deep links are all optional at build time: each switches on when its config exists (ADR 0010, docs/going-live.md).

Area File(s) Change Why Expo (Track B) equivalent
Firebase (Android) android/build.gradle, android/app/build.gradle Classpath google-services 4.5.0 + firebase-crashlytics-gradle 3.0.8 (the versions in @react-native-firebase/app’s sdkVersions). Both plugins apply only when some flavor has app/src/<flavor>/google-services.json; missingGoogleServicesStrategy = WARN lets other flavors build; their uploadCrashlyticsMappingFile* tasks are disabled The template must build before a Firebase project exists. Checked on AGP 9 with a placeholder config @react-native-firebase/app config plugin + googleServicesFile
Firebase (iOS) ios/Podfile use_frameworks! :linkage => :static (overridable with USE_FRAMEWORKS), $RNFirebaseAsStaticFramework = true, $RNFirebaseDisableSPM = true React Native Firebase refuses SPM with static frameworks, and its SPM mode needs dynamic frameworks that each embed a private Firebase copy; CocoaPods is its recommended setup. Firebase stops publishing to CocoaPods after October 2026: 12.18 stays installable; follow React Native Firebase’s migration when it moves expo-build-properties useFrameworks: "static"
Firebase config + Crashlytics (iOS) scripts/ios/configure-environments.rb → project.pbxproj; react-native.config.js Build phase “[bareship] Copy Firebase config” bundles ios/Config/Firebase/<env>/GoogleService-Info.plist when present. “[bareship] Crashlytics symbols” uploads dSYMs for Release builds with that plist. React Native Firebase’s own “[RNFB] Crashlytics Configuration” phase is turned off (scriptPhases: []) RNFB’s phase fails every build without a plist (found in Phase 0) Config plugin
Firebase start (iOS) AppDelegate.swift FirebaseApp.configure() only when the plist is bundled configure() crashes without it Config plugin
App delegate window (iOS) AppDelegate.swift, SceneDelegate.swift var window: UIWindow? on the app delegate, set by SceneDelegate to the scene’s window React Native Firebase Messaging reads appDelegate.window at launch: the app crashed with an unrecognized selector under the UIScene life cycle (found on the simulator 2026-10-11) Handled by Expo’s app delegate
Firebase defaults firebase.json No Crashlytics reports from debug builds; FCM’s default Android channel is default Read by RNFB at build time Same file
Deep links (Android) AndroidManifest.xml, android/app/build.gradle, android/app/src/appLinks/AndroidManifest.xml VIEW intent filter for ${appScheme} (from APP_SCHEME in .env.<flavor>). Flavors whose .env sets APP_LINK_DOMAIN also merge an autoVerify https filter Per-environment schemes; verified links only for domains you control scheme + intentFilters in app config
Deep links + push (iOS) scripts/ios/configure-environments.rb → ios/Config/<Env>.xcconfig, Info.plist, ios/BareshipApp/BareshipApp-<Env>.entitlements APP_URL_SCHEME per environment; CFBundleURLTypes; UIBackgroundModes: remote-notification. An entitlements file (associated domains, aps-environment) only when APP_LINK_DOMAIN / IOS_APS_ENVIRONMENT is set Both entitlements need a paid Apple team; adding them unconditionally breaks signing with a free account scheme, ios.associatedDomains, ios.entitlements
Notifications permission (Android) AndroidManifest.xml POST_NOTIFICATIONS Android 13+ asks at runtime Config plugin
Encoding scripts/ios/configure-environments.rb Encoding.default_external = UTF_8 Ruby read build.gradle as ASCII in shells without a UTF-8 locale and crashed n/a
  • iOS: 'RCTRootView' is deprecated in SceneDelegate.swift. This comes from react-native-bootsplash’s documented customize(_ rootView:) hook. Track the library’s update; it is not an error.
  • Re-check SceneDelegate.swift when upgrading to RN 0.88: its template ships an official SceneDelegate with startReactNative(withModuleName:in:connectionOptions:) and RCTLinkingManager scene helpers. Switch to those.