Build & Release
Release the Storefront App — app identity, icons, versions, signing, TestFlight and the App Store, Google Play, and the web build — with a checklist for each release.
Build & Release
Before the first release
| What | Where |
|---|---|
| The app's name | APP_NAME in .env (iOS reads it into Info.plist; check General → Display Name in Xcode) |
| Android package name | APP_IDENTIFIER in .env |
| iOS bundle id and team | Xcode → the StorefrontApp target → Signing & Capabilities (both Debug and Release) |
| Icon and launch screen | brand/app-icon.png and brand/splash-screen.png, then yarn generate:app-icon and yarn generate:launch-screen (Branding) |
| Keys and host | .env for production (Configuration) |
| Sign-in | Apple and Google set up for the release bundle id and signing key (Sign-in) |
| Push | APNs and FCM channels in the Console, google-services.json in android/app/ (Push Notifications) |
| Store review | A review account and code on the server (Sign-in → review), and the review notes in App Store Connect and the Play Console |
iOS: TestFlight and the App Store
Pods. After pulling a release or changing native dependencies: yarn pod:install.
Version. In Xcode, set the Version (marketing version, e.g. 1.3.0) and Build number. Each upload needs a build number higher than the last one App Store Connect has for that version, and a new version must be higher than the one live.
Node for Xcode. The archive bundles the JavaScript with Node. If Xcode finds an old Node, point it at Node 20 in ios/.xcode.env.local: export NODE_BINARY=/path/to/node20/bin/node.
Archive. Open ios/StorefrontApp.xcworkspace (the workspace, not the project), choose Any iOS Device (arm64), then Product → Archive.
Upload. In the Organizer: Distribute App → App Store Connect → Upload. The build appears in TestFlight once Apple has processed it.
Warnings that a dSYM is missing for React.framework, ReactNativeDependencies.framework or hermesvm.framework don't stop the upload: those frameworks ship prebuilt without debug symbols.
Android: Google Play
Version. Set ANDROID_VERSION_CODE (higher than every previous upload) and ANDROID_VERSION_NAME in .env.
Signing. Create an upload keystore once, keep it safe, and give the build its path and passwords:
ANDROID_STOREFRONT_APP_UPLOAD_STORE_FILE=/path/to/upload.keystore
ANDROID_STOREFRONT_APP_UPLOAD_STORE_PASSWORD=…
ANDROID_STOREFRONT_APP_UPLOAD_KEY_ALIAS=upload
ANDROID_STOREFRONT_APP_UPLOAD_KEY_PASSWORD=…As environment variables or Gradle properties. Without them the release build is signed with the debug key, which is fine for testing but not for Google Play.
Build an app bundle for Google Play:
cd android
./gradlew bundleRelease # android/app/build/outputs/bundle/release/app-release.aab
./gradlew assembleRelease # an APK, for testingUpload the .aab in the Play Console. With Play App Signing, add the app signing key's SHA-1 (Play Console → App integrity) to Google sign-in's Android client.
Web
yarn web:buildWrites a production bundle to dist/. Serve it from any static host. Maps on the web need the Maps JavaScript API on the Google Maps key.
Releases of the app itself
New versions of storefront-app are published as GitHub releases with notes, including any breaking changes to branding options, screen ids and the components and hooks client screens use. A branded build takes a release by merging it (Client Builds). Every release also refreshes the screenshots on this site: they are captured from the release on the iOS Simulator and the Android Emulator against demo data.
Under the hood
- React Native 0.86 with the New Architecture and Hermes on both platforms. Native modules you add must support the New Architecture.
- Minimums: iOS as set by React Native 0.86; Android 7.0 (API 24), built against API 36.
- Patches in
patches/are applied onyarn install(@backpackapp-io/react-native-toast,@stripe/stripe-react-native,react-native-i18n,react-native-notifications). Remove or regenerate one when upgrading its package. - The web build swaps native-only packages (maps, blur, bottom sheet, gradients, device info, Google sign-in and others) for web versions in
web/, set up inwebpack.config.js. A new native dependency needs the same for the web build to compile. - Checks:
yarn lint:strict,yarn typecheck:strictandyarn testrun in CI on every pull request, along with iOS and Android builds.