Articles · GuidesUpdated October 2026

You cannot make a phone update your app. You can only stop serving the old one.

Android gives you a documented API for this. Google Play In-App Updates has an immediate flow that fills the screen, installs the new version and restarts the app. Apple publishes nothing of the kind. On iOS the only thing that can require a new version is code you wrote: the app asks your server what the minimum supported version is, compares it to its own, and refuses to go further on anything older. Even on Android you still need that check, because the immediate flow is a request the user can cancel. Most people meet this problem a few weeks after publishing to the App Store, the first time a release breaks something an old client depends on.

What follows is what Apple actually documents, what Google's API does and does not promise, which of the two flows to reach for, and how to write a gate that neither gets rejected nor locks out a reviewer. Every figure, field name and screen name below was read from Apple's and Google's own pages on 16 October 2026.

See what Apple documents

The short version

One store ships the blocking dialog. Neither one ships the decision.

The job splits into two halves that people keep merging. One half is telling the user, which is a dialog, a banner or a wall of text with a button on it. The other half is deciding, which is a number on your server that says what you still support. Google hands you a good version of the first half and nothing at all of the second. Apple hands you neither.

That is why almost every real answer looks the same on both platforms. The app calls your server on launch. The server answers with a minimum version. The app does nothing, nudges, or blocks. On Android you then let Google's API do the installing, because it is better at it than a link to the store listing. That is the whole design, and it fits in an afternoon.

Does Apple document any way to force an update?

No. There is no switch in App Store Connect and no framework call that requires a customer to move. The nearest thing Apple publishes is a pacing control, not a push. When you release a version update you can send it out in stages, and Apple's help page sets the schedule for you: seven days, reaching 1 percent of users on day one, then 2, 5, 10, 20, 50 and 100 percent. The people in it are a random sample of users with automatic updates on eligible devices, and Apple says they get it without any notification of their participation in the phased release. The field details of running one are in a phased release.

Read those terms again and the limits jump out. It only reaches people who left automatic updates switched on, which is a setting you do not control. It only goes one way, towards more users, and the fastest it moves is the button marked Release to All Users, available at any time once the version has the Ready for Distribution status. You can pause a release for a total of 30 days, with no limit on the number of pauses, which is a brake and not an accelerator. And if you remove the app from sale, including when your developer membership lapses, the phased release stops and is not available again for that version.

One line on that page matters more than the percentages. Apple states that apps and app updates in phased release can be manually downloaded from the App Store by anyone at any time. So the percentage is a ceiling on automatic installs, never a lock on the new build. Anyone you send to your product page can take the new version straight away, on day one of a seven day rollout. That is the fact your blocking screen rests on, and without it a gate would be cruel: you would be telling people to update to something they could not yet get.

The review guidelines are quiet on the subject. Read on 16 October 2026, they do not mention forced updates, version gates or blocking screens anywhere, so there is nothing to break and nothing to cite as permission. What they do carry is pressure in the opposite direction. The Design section says apps that stop working or offer a degraded experience may be removed from the App Store at any time, and 2.5.1 tells you to keep your apps up to date and phase out deprecated features and frameworks. Apple's position, in effect, is that keeping old versions working is your problem.

App Store Connect Help, release a version update in phases, read 16 October 2026

What does Google Play's in-app update API actually do?

It gives you two flows, and the difference is who is in control while the download runs. A flexible update opens a consent dialog first. If the user agrees, the download starts in the background and they carry on using the app. Google Play does not automatically trigger a restart for a flexible update, so the last step is yours: when you see the DOWNLOADED install status, you ask the person to restart. Call completeUpdate() in the foreground and the platform displays a fullscreen UI that restarts the app in the background. Call it while your app is in the background and the update installs silently, without covering the device UI.

An immediate update is the one people mean when they say forced. Google describes it as a fullscreen flow that requires the user to update and restart the app in order to continue using it, and says that once the user accepts, Google Play handles the update installation and the app restart. Progress sits on top of your UI for the whole update. If the person closes or terminates your app during it, the download and install should continue in the background without further confirmation. The one thing you have to add is a check on resume: if the state is a developer triggered update already in progress, the update may be stalled, and you restart the flow.

Now the part that gets missed. Even an immediate update is a request. The flow returns a canceled result when the user denies or cancels it, and a failed result when something else got in the way, and Google tells you that you can request to start the update again. So it blocks the screen, not the app. Someone who taps the back gesture is back in your old build with your old bug. If the old version truly must not run, the no has to come from your own server, and the Play API becomes the nicest way to carry out a decision you already made.

Two practical limits before you plan around it. Google's in-app updates overview, read on 16 October 2026, says the feature is supported on devices running Android 5.0, API level 21, or higher, and only on Android mobile devices, Android tablets and ChromeOS devices, and that it is not compatible with apps that use APK expansion files. Testing it is fiddly in a way that eats an afternoon if nobody warns you. The installed app has to come from an internal app sharing URL, carry the same application ID and signing key as the Play version, and have a lower version code than the update. The account has to have downloaded your app from Google Play at least once. And update priority is not supported for internal app sharing uploads, so you cannot rehearse that part.

Android Developers, support in-app updates, updated 1 October 2026, read 16 October 2026

Pick your case

Which lever this actually calls for

Tick what changed in the new version. The lever is rarely the same on the two stores, and it is rarely the loudest one.

Nothing ticked. If nothing in the release breaks a promise the old version made, you do not need any of this. Ship it and let automatic updates do the work.

Which update flow should you pick, and when?

Google ships two dials for exactly this question, and one of them has a trap in it. Update priority is an integer from 0 to 5, where 0 is the default and 5 is the highest. You set it with the inAppUpdatePriority field under Edits.tracks.releases in the Google Play Developer API, and every newly added version in that release takes the release's priority. The trap is the next sentence in Google's own documentation: priority can only be set when rolling out a new release, and cannot be changed later. You cannot discover on Thursday that Monday's build was urgent. Decide at rollout, and when in doubt about a release that touches data, set it higher than feels comfortable.

The second dial is staleness. In the app, clientVersionStalenessDays gives you the number of days since the update became available on the Play Store, and updatePriority gives you the priority, taking into account every version code between the installed one and the latest, whatever track it came from. Google's own suggested shape uses both: wait a few days before notifying the user with a flexible update, and a few days after that before requiring an immediate one. That ladder is the right default. It keeps the fullscreen flow for the cases that earn it.

The cases that earn it have one thing in common. The old version now does something wrong rather than something less. An endpoint it calls is gone. It writes data in a shape your server no longer accepts. It crashes on launch on one OS version. Or it handles money incorrectly, which is the most common reason the ladder gets skipped: if you have been adding payments and receipt validation changed, an old client can take a payment and record nothing, and every day you leave it running is a refund conversation later.

Everything else is a nudge. A new feature, a faster screen, a redesign, a fix for a bug nobody reported: these do not justify standing between a person and the app they opened. Be honest about which list your release is on, because users are good at spotting a fullscreen update wall that exists for your analytics rather than their benefit, and the review it earns says so.

Android Developers, update priority and staleness, read 16 October 2026

How do you write a version gate that survives review?

Keep the number on the server. A minimum version compiled into the app cannot be changed without shipping another app, which is the exact problem you are trying to solve. The shape that works is small: on launch the app sends its own build number, the server answers with a minimum supported version and an optional recommended one, and the app picks one of three states. Nothing. A dismissible nudge. Or a blocking screen with one button on it. Three states, one request, and a sensible default if the request fails.

Three things sink gates in practice. The first is review. A reviewer runs the build you submitted, and if your staging server answers with a minimum higher than that build, they see a wall instead of an app. Guideline 2.1, App Completeness, read on 16 October 2026, asks for final versions, tested on device, with demo account details when there is a login, and says incomplete bundles and binaries that exhibit obvious technical problems get rejected. A gate firing on the submitted build is an obvious technical problem. Pin the minimum per environment, and check what production answers for the build you are about to submit.

The second is the button. It has to open the place the new version lives, which is your App Store product page or your Play listing, and nothing else. This is where Apple's manual download line earns its keep: the new version is available to anyone from the product page at any time, even on day one of a seven day phased rollout, so a person following your button is never sent somewhere empty. Do not point the button at a support article. Do not ask them to wait.

The third is reaching for remote code instead. It is tempting to avoid the whole cycle by downloading new behaviour at runtime, and Apple's guideline 2.5.2 is the boundary: apps should be self-contained in their bundles and may not download, install or execute code which introduces or changes features or functionality of the app. An over-the-air update of your JavaScript and assets is a different thing and has its place, covered below, but a native change is a new binary, a new review and a new number for your gate to compare against. If that cycle is the thing you are flinching from, the honest cost of it is in what it costs to publish an app.

Last, decide what happens when the check itself fails. A phone on a hotel wifi captive portal gets no answer from your server. Fail open, every time: no answer means no gate, and the app behaves as it did before. A gate that locks people out when your own endpoint is down turns a small outage into a one star review, and you will not be able to ship the fix any faster than the store allows.

App Store Connect Help, phased release and manual downloads, read 16 October 2026

What each store hands you, and what it leaves to you

The leverApp StoreGoogle Play
A fullscreen flow that requires the updateNothing documentedAppUpdateType.IMMEDIATE, Play installs it and restarts the app
A background download the user can ignoreAutomatic updates, if the user left them switched onAppUpdateType.FLEXIBLE, and your code calls completeUpdate()
An urgency signal the app can readNothing documentedupdatePriority(), 0 to 5, fixed when you roll out
Days since the new version shippedNothing documentedclientVersionStalenessDays()
The user can fetch it right now, by handYes, from the product page, at any timeYes
The old version keeps running if they say noYesYes
A minimum supported version on your serverThe only thing that genuinely requires an updateStill needed, behind the API

Where this lands if a builder generated your app

It lands on you, and the guideline says why. App Store Review Guideline 4.2.6: apps created from a commercialized template or app generation service will be rejected unless they are submitted directly by the provider of the app's content. The release cadence is therefore yours, which also means the gate is yours. No builder can decide on your behalf which of your customers is on a version you no longer support.

Newly is an AI app builder. You describe the app in plain English and it writes a real React Native and Expo project that you own, with two-way GitHub sync, so a version check is a file you can read and change. It uploads iOS builds to TestFlight, publishes to Google Play internal testing and builds a standalone Android APK, and it does not submit to the App Store for you, for the reason above. It is $25 a month, with no free tier.

Being an Expo project changes one row of this problem. Expo's documentation is clear about what an over-the-air update can carry, meaning JavaScript, styling and images, and what it cannot: native code, native dependencies, app permissions, the Expo SDK version, and anything that needs a new app binary. It also notes that your updates still have to follow the App Store and Play Store guidelines. So a wrong price label or a confusing screen can be fixed in minutes with nobody updating anything. A new camera permission cannot. Knowing which side a change falls on is most of the skill here.

While you are still in beta there is one real force available, and Apple documents it. In App Store Connect, open the app, click the TestFlight tab, pick the platform under Builds, open the build and click Expire Build. Apple says the build then no longer allows internal and external testers to install it, and the step needs the Account Holder, an Admin or an App Manager. That is the cleanest way to get a group of testers off a build with a bad bug in it. Once a version is on the App Store, no equivalent exists, and the gate in your own code is all you have.

Questions people ask about forcing an update

Not through the App Store. Apple documents no setting and no API that moves a customer onto a new version. The only thing that can require it is code in your own app: it asks your server for a minimum supported version on launch and refuses to continue on anything older. Apple's one release control is phased release, which paces automatic downloads over seven days and can be paused, and Apple says apps and app updates in phased release can be manually downloaded from the App Store by anyone at any time. Checked on 16 October 2026.

Build the gate before you need it

One endpoint that returns a minimum version, and three states in the app. It takes an afternoon in version one and it is miserable to retrofit in version nine, which is usually when the reason for wanting it arrives.

Start building