Phase 0
Before you start
Accounts, machines and tools
Store accounts take real time to approve and cost real money. Start these on day one so nothing blocks you at the end. Everything below is a one-time setup per developer, not per app.
Your web app stays exactly as it is. Capacitor wraps the built files in a thin native shell — an Android project and an iOS project — that opens your app in a full-screen system web view and hands it a bridge to native APIs (camera, push, files, biometrics). The stores receive a normal native app package; inside it, your web app runs.
- One codebase. The same React app powers web, Android and iOS.
- Two extra folders.
android/andios/are generated native projects that live in your repository. - Native build steps stay native. Android builds with Gradle/Android Studio, iOS builds with Xcode on a Mac. There is no way around that.
- Store review is a human process. Plan for 1–7 days on Apple and hours-to-days on Google, plus the mandatory testing period for new Play accounts.
This is the primary target. Registration costs a one-time $25 USD and requires identity verification — government ID, address, and for organisations a D-U-N-S number. Verification is the slow part; it can take a few days.
- Go to the Play Console signup and choose Personal or Organisation. Choose carefully — switching later means a new account.
- Pay the $25 fee with a card matching your identity details.
- Complete identity verification and, for personal accounts, provide a phone number and address that match your ID.
- Set up a payments profile if you plan to sell anything or run ads.
The Apple Developer Program costs $99 USD per year and must be renewed or your apps are removed from sale. Enrolment is done in the Apple Developer app or on the website, and usually completes within 48 hours; organisations need a D-U-N-S number, which can take longer.
- Individual enrolment publishes under your legal name — visible on the store listing.
- Organisation enrolment publishes under your company name and requires a legal entity plus D-U-N-S.
- You need an Apple ID with two-factor authentication enabled.
Android builds on Windows, macOS or Linux. iOS builds require macOS and Xcode — this is an Apple restriction with no legitimate workaround. Your options:
| Option | Cost | Good for |
|---|---|---|
| Own a Mac (M-series) | One-time hardware | Regular releases, debugging on device |
| Mac cloud (MacStadium, Scaleway) | Hourly/monthly rental | Occasional releases |
| CI service (Codemagic, Bitrise, Xcode Cloud, GitHub Actions macOS runners) | Free tier then per-minute | Automated builds, no local Mac |
| Tool | Version | Why |
|---|---|---|
| Node.js | 20 LTS or 22 LTS | Builds your web app and runs the Capacitor CLI |
| Git | any recent | Your code lives in GitHub |
| Android Studio | latest stable | Android SDK, emulator, Gradle, signing UI |
| JDK | 17 (bundled with Android Studio) | Gradle requires it; do not use 8 or 11 |
| Xcode | latest stable (Mac only) | iOS build, archive, upload |
| CocoaPods | latest (Mac only) | iOS native dependencies |
Verify everything in one go:
node -v # expect v20.x or v22.x
npm -v
git --version
java -version # expect 17.x
# macOS only
xcodebuild -version
pod --version# ~/.zshrc or ~/.bashrc
export ANDROID_HOME=$HOME/Library/Android/sdk # Linux: $HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
export PATH=$PATH:$ANDROID_HOME/emulatorYour application ID (Android) / bundle identifier (iOS) is permanent. Once an app is published under com.example.myapp, that string can never change — a new ID means a new listing with zero installs and zero reviews. Choose it as if it were a domain name.
- Reverse-domain form:
com.yourcompany.yourapp. - Lowercase letters, digits and dots only. At least two segments. No hyphens, no leading digits in a segment, no reserved Java words (
new,class,int). - Never ship
com.example.*— Play rejects it. - Use the same string on both platforms so your analytics, deep links and push tokens line up.
Configuration generator
Saved in this browser and substituted into every command and snippet in the guide.
import type { CapacitorConfig } from "@capacitor/cli";
const config: CapacitorConfig = {
appId: "com.example.myapp",
appName: "My App",
webDir: "dist",
android: {
allowMixedContent: false,
},
ios: {
contentInset: "always",
},
plugins: {
SplashScreen: {
launchAutoHide: false,
backgroundColor: "#ffffff",
androidScaleType: "CENTER_CROP",
showSpinner: false,
},
Keyboard: {
resize: "body",
resizeOnFullScreen: true,
},
},
};
export default config;