Building and signing app releases

Play this lesson

From code to a store file

Remy can already run Garden Defense on a phone plugged into the laptop. But the stores won't take that version. They want a release build: a finished app file, numbered and signed with the studio's secret key.

Garden Defense is a web game inside CapacitorJS, a wrapper that turns web code into an Android project and an iPhone project. This lesson follows our code all the way to the files Google and Apple accept.

Debug vs release builds

Android Studio (for Android) and Xcode (for iPhone) can build the same code in two ways.

Always play the release build itself before shipping. Shrinking files and switching off tools can change how an app behaves, so a bug can hide in the release build only.

Quick check

Remy is about to upload Garden Defense to Google Play. Which build should it be?

  1. A debug build, because it has the most tools
  2. A release build signed with the studio's key
  3. Any build, as long as the game runs
Show the answer

A release build signed with the studio's key

Stores only accept release builds signed with our own key. Google Play rejects apps that are still debuggable, because debug tools would let anyone poke around inside.

Two version numbers

Every release has two numbers. The version name, like 1.2.0, is for players and shows on the store page. The version code on Android, called the build number on iPhone, is a plain whole number like 7 that the store uses to tell which build is newest.

The rule: every upload gets a new, higher code, and a code can never be reused, even by a build that never went public. The app's ID, like com.ourstudio.gardendefense, never changes once published, or the store treats it as a brand-new app.

Version bump

android/app/build.gradle
android {    defaultConfig {        applicationId "com.ourstudio.gardendefense"        versionCode 7        versionName "1.2.0"    }}

Play Console says: "Version code 7 has already been used." Remy is shipping the 1.2.1 hotfix. What should lines 4 and 5 become?

  1. versionCode 8 and versionName "1.2.1"
  2. versionCode 7 and versionName "1.2.1"
  3. versionCode 8 and a new applicationId
Show the answer

versionCode 8 and versionName "1.2.1"

The version code must go up with every upload, and the version name tells players it's the 1.2.1 hotfix. Changing the applicationId would turn it into a different app.

From web code to an App Bundle

First we build the web game. Then npx cap sync copies those fresh files into the Android and iPhone projects and updates the plugins. npx cap open android opens Android Studio, where Build, Generate Signed App Bundle makes the release file.

That file is an Android App Bundle (.aab), which Google Play requires for new apps. Phones never install it directly. Google Play cuts it into small APKs (the files phones actually install), each made for one kind of screen, chip and language, so downloads stay small.

Bug hunt

Remy's terminal
$ npm run build         # web files saved to dist/$ npx cap open android  # opens Android Studio# Build > Generate Signed App Bundle# Saved: app-release.aab (versionCode 8)

Remy fixed wave5.js and made a new .aab, but testers still see the old bug. Which command did Remy skip?

  1. npx cap sync, between line 1 and line 2
  2. git push, after line 4
  3. npm install, before line 1
Show the answer

npx cap sync, between line 1 and line 2

npm run build makes fresh web files, but the Android project keeps its old copies until npx cap sync copies them in. The order is build, sync, then open and make the bundle.

The signing key

A signing key is a secret file (a keystore, like garden-release.jks) locked with a password. Signing stamps each build with it, and a phone only accepts an update whose stamp matches the installed app.

Spot the leak

Kai's terminal
$ git statusChanges to be committed:  modified:   src/wave5.js  modified:   android/app/build.gradle  new file:   android/app/garden-release.jks  new file:   android/keystore.properties

Kai is about to commit the 1.2.1 hotfix. What should Kai do first?

  1. Unstage lines 5 and 6, add those files to .gitignore, and keep the key backed up safely
  2. Commit everything, because the repository is private
  3. Delete garden-release.jks so nobody can ever steal it
Show the answer

Unstage lines 5 and 6, add those files to .gitignore, and keep the key backed up safely

Line 5 is the signing key and line 6 usually holds its passwords. Private repositories still get shared and copied, and deleting the key would mean losing it, the other disaster.

iPhone builds need a Mac

Apple builds need Xcode, which only runs on a Mac. npx cap open ios opens our iPhone project there.

Apple signing has two parts. A certificate is a digital ID proving a build came from our Apple Developer account. A provisioning profile is a permission slip that ties our app ID and certificate to where the app may run: test phones or the App Store. Xcode's Automatically manage signing creates both. Then Product, Archive makes the release, ready to upload to App Store Connect, Apple's website for our apps.

Bolt says: We have release builds. Before any of them reaches a store, the whole team checks them properly. Next: QA passes and bug tracking.

Fact-checked 9 October 2026