versionCode is an integer Play uses to order builds and decide what is an update: it must grow with every upload and can never be reused. versionName is the free-form string users see, and Play does not check it.
**versionCode**: unique per package across all tracks, at most 2100000000; a number stays spent even if that release was deleted or never shipped
Version only goes up: Android refuses to install a lower versionCode over a higher one, so re-uploading an old bundle cannot roll anything back
**versionName**: for example "8.4.0"; it can repeat and exists for users and support tickets
iOS has the same pair: CFBundleVersion (build number) and CFBundleShortVersionString (marketing version)
AndroidMiddle
Release & Store Review
App Store Connect, Play Console, review rejections, phased release, forced update, policy
Questions closed
0 / 16
- 0Review
Questions
Play has internal, closed and open testing tracks plus production: internal testing reaches up to 100 testers within minutes, while closed and open releases are reviewed like production. TestFlight splits the same way into internal and external testers.
Internal testing: up to 100 testers from an email list, available minutes after upload; for QA and the team
Closed and open testing: closed is for invited email lists or Google Groups (several closed tracks are allowed); open lets anyone join from the store listing as a public beta; both are reviewed before testers see them
Internal app sharing: outside the tracks: upload any AAB or APK, get a link, no versionCode rules; handy for testing a branch
TestFlight: internal testers (up to 100 App Store Connect users) get a build as soon as processing ends; external testers (up to 10,000) wait for Beta App Review on the first build of a version; every build expires after 90 days
Personal developer accounts created after 13 November 2023 must first run a closed test with at least 12 testers who stay opted in for 14 consecutive days, and then apply for production access in Play Console.
Testers must stay in: they opt in to the closed track and remain opted in for the whole 14 days (the bar was 20 testers until December 2024); the internal testing track does not count
The application: a questionnaire about how you recruited testers, what feedback you got and what you changed; thin answers get refused and you test again
Organization accounts are exempt: a company account verified with a D-U-N-S number goes straight to production, one reason teams publish under an organization
An upload only hands the binary to the store: it is then processed, reviewed and released, and it can stall or be rejected at each step. Approved does not mean available.
Processing: Play validates the AAB and generates APKs in minutes; Apple re-signs and thins the build, usually 5–30 minutes, and reports validation errors by email as ITMS-… codes
Pre-launch report: for testing tracks Play runs the build on real devices in Firebase Test Lab, so crashes surface before a reviewer sees them
Review: Apple usually answers within 24 hours; Play is often hours for an update but can take up to a week, longer for a new app or a sensitive permission
Release: Apple releases automatically, manually or on a date; Play publishes on approval unless managed publishing holds it
Rollout: a staged or phased release still ramps over days, and each device updates on its own schedule
An APK is an installable package; an AAB is a publishing format that cannot be installed. Play turns it into a base APK plus split APKs per ABI, screen density and language, signs them with the app signing key and serves each device only what it needs.
Required for new apps: since August 2021; APK uploads remain for older apps and other stores
Smaller downloads: a phone gets one ABI and one density instead of all of them; Google reports about 15% less on average, much more for apps with several native ABIs
Play App Signing is mandatory: you sign the AAB with an upload key and Play re-signs with the app signing key it holds, so the SHA-256 for Firebase, Maps and App Links comes from Play Console
Play Feature Delivery: dynamic feature modules delivered at install time, on a condition, or on demand through SplitInstallManager
Play Asset Delivery: large assets such as game data or ML models go in asset packs, outside the roughly 200 MB compressed limit on the base download
Other stores and QA: build APKs locally with bundletool; --mode=universal produces one APK with everything
On Play the store listing is published separately from releases, so new text and screenshots go live after a short listing review. On the App Store most metadata belongs to an app version, so you submit a new version even if the code has not changed.
Play: description, icon, feature graphic and screenshots are reviewed and published with no AAB; managed publishing lets you hold them until a chosen moment
App Store, editable any time: promotional text (170 characters), price and availability, privacy answers
App Store, needs a version: name, subtitle, description, keywords, screenshots and What's New; that version is reviewed and can be rejected
No binary, still reviewed: Apple custom product pages and in-app events, Play custom store listings and store listing experiments
Keep it in the repo: fastlane deliver and supply store metadata as files, so a listing change becomes a reviewable diff
Every year Play requires new apps and updates to target a recent API level: from 31 August 2026 that is API 36 (Android 16). Miss it and Play refuses the upload, and an app that falls further behind stops being offered to new users on newer devices.
Why it exists: targetSdk opts the app into newer platform rules; the deadline stops apps living on old ones
A behaviour switch, not a number: targeting 36 removes the edge-to-edge opt-out that 35 still allowed, turns on predictive back system animations by default and ignores orientation and resizability locks on screens 600dp and wider
**minSdk stays your choice: the store never forces it; users below your floor keep the last build that supported them
Extensions: Play Console lets you request extra time, usually until 1 November
Preparing**: read the behaviour changes from the first developer preview, raise compileSdk early, test with the compatibility flags in developer options, and check third-party SDKs, which often lag
Other deadlines ride along: for example 16 KB memory page support for native libraries, required for updates targeting Android 15+ since November 2025
Play's staged rollout ships to a percentage of users that you raise by hand; Apple's phased release spreads an update over seven days on a fixed schedule. Halting or pausing stops new users from getting it, but never removes it from phones that already updated.
Play percentages: any value, raised whenever the metrics look good; to stop, halt, then resume at a new percentage or replace the release
Play halt: users who have not updated and new installs stay on the previous release; users who already updated keep the bad build
Apple schedule: 1, 2, 5, 10, 20, 50, 100% of users with automatic updates on; anyone can still update manually from the store on day one
Apple controls: pause for up to 30 days in total, resume, or release to everyone; no custom percentages
What to watch: crash and ANR rate for the new versionCode and fresh ratings before each step
Neither is a rollback: updated users are fixed only by a new build with a higher version number or by a server-side switch
You request a ReviewInfo from ReviewManager ahead of time and call launchReviewFlow at a natural pause; Play decides whether to show the dialog under a hidden quota and never tells you whether the user rated.
Quota: undisclosed and time-based, so a call can show nothing; a "Rate us" button needs a fallback that opens the store page
No result: the task completes the same way whether the dialog showed, the user rated or dismissed it; continue your flow regardless
Policy: no question like "Do you enjoy the app?" before the dialog to send only happy users to it, and no rewards for reviews
When: after a completed task such as a delivered order or a finished level, not at launch or mid-flow
Testing: the dialog appears only for apps installed from Play: use internal app sharing or internal testing, and FakeReviewManager in unit tests
iOS: requestReview is also system-limited, to three prompts per 365 days
Most rejections are not about code: the reviewer cannot sign in, the metadata does not match the app, a permission or data practice is undeclared, or digital goods bypass store billing. A checklist run against the release build catches nearly all of them.
Reviewer cannot get in: missing or broken credentials in App access (Play) or App Review Information (Apple); give an account that skips OTP and works from outside your network
Sensitive permissions: QUERY_ALL_PACKAGES, MANAGE_EXTERNAL_STORAGE, SMS and call log, background location need a declaration and a core use case on Play
Data safety or privacy label mismatch: an SDK collects something the form does not list
No account deletion: both stores require deleting an account created in the app from inside the app, and Play also wants a web link
Digital goods outside store billing: Apple 3.1.1 and Play's Payments policy, except where regional rules allow alternatives
Broken or unfinished app: crashes on the reviewer's device, placeholder content, dead links (Apple 2.1); on Play, read the pre-launch report from a testing track first
Misleading metadata: screenshots of features that are not there, competitor names in keywords (Apple 2.3)
First reproduce what the reviewer saw on the exact submitted build with a fresh account: most rejections that look wrong come from a reviewer who could not reach the feature. Then reply in the review thread with evidence, and appeal formally only if that fails.
Read what the reviewer saw, not just the guideline number: Apple attaches screenshots plus the device and OS version; Play's email names the policy and often the offending element
Fix the reviewer's path: working demo credentials and numbered steps to the feature resolve more rejections than code changes
Apple: reply in App Store Connect: answer in the App Review messages with a short screen recording; a new binary restarts the queue with a different reviewer
Apple: App Review Board: a separate appeal form, slower than fixing and resubmitting
Play: appeal from Policy status: or submit a fixed update
Ship the fix in parallel: if a small change satisfies the reviewer, it is often faster than winning the argument
Play returns details only for Active products that belong to the same applicationId the build runs under, in an app whose build carrying the Billing Library is uploaded to a track; a payments profile is also needed.
Product status: a product left in Draft or Inactive is not returned; activate it in Play Console
Build and tester: the build must be uploaded to a track, and the tester must be a license tester who installed the app from that track
Wrong package: a debug build with an applicationIdSuffix asks Play for the products of a different app
Handle empty: show an error with Retry instead of a blank paywall; reviewers reject a paywall that does nothing
Apple equivalent: the Paid Apps Agreement must be active and the first product attached to an app version; products stuck in Missing Metadata return an empty array from Product.products(for:)
Keep the minimum supported version on your server and check it at startup; on Android the Play In-App Updates API can then install the update through the immediate flow. iOS has no such API, so the app shows a blocking screen with a link to the App Store.
Floor from the server: Remote Config or your own API; the check must ship in an early version, because a build already out cannot gain a new gate
Immediate vs flexible: immediate is full-screen and restarts the app; flexible downloads in the background and you call completeUpdate() when ready
Priority and staleness: inAppUpdatePriority (0–5) is set per release through the Publishing API and read with updatePriority(); clientVersionStalenessDays() lets you escalate from flexible to immediate
Play must offer the update: sideloaded installs, a halted rollout or a 5% rollout that has not reached this user get nothing; fall back to your own screen with a store link
Do not trap users: if the new build raised minSdk, older devices can never update; exempt them by OS version or keep the old API alive
iOS: compare with the server floor and open itms-apps://apps.apple.com/app/id…; the iTunes lookup API is cached and lags on release day
Neither store can uninstall or downgrade a build already on phones, so the first hour is about stopping new damage (a server-side switch, halting the rollout) while a hotfix with a higher version number goes through review.
First hour
Remote switch: the only lever that reaches phones already on the bad build; for a launch crash it helps only if the flag is read before the crashing code
Play: halt the release so new installs fall back to the previous one; once the fix is out, app recovery in Play Console can target users stuck on the bad versionCode with an update prompt
App Store: pause the phased release if it is still running; Remove from Sale only hides the listing and does nothing to installed apps
The hotfix
Numbers only go up: a new versionCode and build number; the previous build cannot be re-published
Expedited review: Apple's form for a critical bug can move the submission up the queue, but it is a favour, not a plan: the build can still be rejected, and Play has no equivalent
Make it urgent: set inAppUpdatePriority 5 on the Play release so the in-app update gate goes immediate
What should have existed
Kill switches around risky features, with defaults that are safe for a build that cannot reach config
A staged rollout watched on crash rate per versionCode before reaching 100%
Scripted store actions: halt and pause through the Play Developer API and App Store Connect API, rehearsed before an incident
Rejection, removal, suspension and termination are different states with different exits: a removed app returns after a compliant update, a suspension needs an appeal and counts as a strike, and termination ends every app on the account.
The states
Rejected: one update is blocked; the live version keeps serving
Removed: the app disappears from the store, existing installs keep working; fix the issue and submit an update
Suspended: removed plus a strike; enough strikes or one severe violation lead to account termination
Recovery
Read the notice: it names the policy and often the offending element: a permission, an SDK, a screenshot. Fix exactly that, then resubmit or appeal from Policy status
Never resubmit unchanged: and never open a second account to get around it; Play links accounts and terminates both
Apple: the equivalent appeal goes to the App Review Board
What should have existed
Shared ownership: the account owner is a company identity with backup admins, not one person's login
Play App Signing: a lost upload key becomes a key reset instead of a new listing with zero installs
Monitoring: installs and Play Console policy messages on a dashboard someone reads, and a way to reach users outside the store
Treat each policy as a dependency with a fixed date: route announcements to a shared inbox, turn each one into a tracked item with an owner the day it appears, and land the change at least two releases before the deadline.
Where they come from: the Play Console inbox and policy updates, the Android Developers blog, Apple Developer News and App Store Connect messages; by default they reach only the account owner
Predictable ones: Play's target API level every 31 August and Apple's minimum Xcode and SDK every spring; put them on the calendar a year ahead
Two kinds: some block new uploads (target API, 16 KB pages), others apply to apps already live (Data safety, account deletion) and can get the app removed
SDKs are part of it: ads, analytics and payment SDKs need updates for most deadlines and often ship them late; track their versions in the same register
Leave review margin: compliance updates get rejected too; landing them two release trains early leaves room for a second attempt
Recent example: Android developer verification: from September 2026, apps installed on certified devices in Brazil, Indonesia, Singapore and Thailand must come from verified developers, even outside Play