iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Publishing

Publishing an Ionic app to the App Store and Google Play means going through Capacitor + the platform-specific signing flow, then submitting via Transporter, Xcode, or the Play Console. The whole pipeline becomes one command per release once fastlane is wired up. The work is one-time; the gains are every release after.

App Store + Play release pipelines with fastlane

EXAMPLE
# ===== Prerequisites =====
# - Apple Developer account (paid)
# - Google Play Console account (paid one-off)
# - macOS for iOS builds (App Store Connect needs Xcode)
# - fastlane installed:  brew install fastlane

# ===== 1) Bump version + build numbers consistently =====
# capacitor.config.ts        version: '1.4.0'
# android/app/build.gradle   versionName '1.4.0' versionCode 142
# ios/App/App/Info.plist     CFBundleShortVersionString 1.4.0 / CFBundleVersion 142
#
# Tip: source the version from a single file (package.json) and template the rest.
# fastlane has the 'increment_version_number' / 'increment_build_number' actions.

# ===== 2) Build the web bundle once =====
ionic build --prod
npx cap sync           # copies web -> ios and android, updates plugins

# ===== iOS — App Store =====
# ios/fastlane/Fastfile
# default_platform(:ios)
# platform :ios do
#   desc 'Build + submit to TestFlight'
#   lane :beta do
#     match(type: 'appstore', readonly: true)   # signing via Match (encrypted git repo)
#     build_app(scheme: 'App', workspace: 'App.xcworkspace', export_method: 'app-store')
#     upload_to_testflight(skip_waiting_for_build_processing: true)
#   end
# end
#
# Run:
cd ios && bundle exec fastlane beta

# Then in App Store Connect:
# - Add release notes per locale
# - Submit for App Review (or auto-release after approval)
# - First release? Prepare screenshots (6.5\" + iPad), privacy nutrition label, support URL.

# ===== Android — Google Play =====
# android/fastlane/Fastfile
# default_platform(:android)
# platform :android do
#   desc 'Build AAB + upload to Internal track'
#   lane :internal do
#     gradle(task: 'bundleRelease')
#     upload_to_play_store(track: 'internal',
#                          aab: '../app/build/outputs/bundle/release/app-release.aab',
#                          json_key: ENV['PLAY_JSON_KEY'])
#   end
#
#   desc 'Promote internal -> production'
#   lane :promote do
#     upload_to_play_store(track: 'production', track_promote_to: 'production',
#                          json_key: ENV['PLAY_JSON_KEY'])
#   end
# end
#
# Run:
cd android && bundle exec fastlane internal
# Promote when you are ready:
cd android && bundle exec fastlane promote

# ===== Asset checklist (App Store + Play) =====
# - App icon (1024x1024 for iOS; 512x512 + adaptive for Android)
# - 6.5\" / 5.5\" screenshots (iOS); phone + 7\"/10\" tablet (Play)
# - Promo text + description per locale
# - Privacy policy URL
# - Data safety form (Play) / privacy nutrition label (App Store)
# - Age rating questionnaire
# - Test account credentials if your app gates features behind login

# ===== Common rejections =====
# - 4.0 Design (iOS): missing legal info, broken links, low value
# - 5.1.1 Privacy: collecting data without a privacy policy
# - 'Missing 64-bit native code' (Play): include arm64-v8a in abiFilters
# - 'Target API too low' (Play): bump targetSdkVersion to the current floor
# - Crash on review device: enable Sentry/Crashlytics, fix BEFORE resubmitting

# ===== CI integration =====
# .github/workflows/release.yml — fastlane runs unattended on macOS runners
# steps:
#   - uses: actions/checkout@v4
#   - uses: ruby/setup-ruby@v1
#   - run: bundle install
#   - run: cd ios && bundle exec fastlane beta
#     env:
#       MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
#       APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.ASC_API_KEY }}

# ===== Post-launch =====
# - Crashlytics on day one (see firebase/crashlytics lesson)
# - Crash-free user rate >= 99.5% before scaling marketing
# - Phased rollout on Play (5% -> 25% -> 100%) for first week
# - TestFlight 'internal' before 'external' to catch obvious regressions

Why it matters

Get fastlane match working on day one. Shared, encrypted signing materials in a git repo means every release can be built from any developer machine or CI runner with no manual key handoffs — and re-issuing a leaked key is a one-command rotate instead of a coordination event across the team.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# iOS → App Store Connect / TestFlight
# Android → Play Console (internal → closed → open → production)
Try it Yourself »

Discussion

Loading…