How to Make Users Update Your App: Force and Flexible Updates
Practice4 minInnovialabs editorial team
In short
On launch the app fetches minimum and latest versions from the server: below minimum shows a blocking update screen, below latest a soft prompt, while the API keeps serving old clients until they move.
The short answer
You need two things built in from the first release:
- A version check on launch. The app gets two values from your backend or remote config:
min_supported_versionandlatest_version. - Two responses. Below the minimum: a hard update that blocks further use. Below the latest: a soft update the user can postpone.
If installed versions lack this check, you cannot "force" them to update; you can only break their API, which is worse. That is why the mechanism goes into the very first version.
Checking the minimum version
Where the values come from:
- Your own backend: a config endpoint or a header in API responses. Full control, separate versions for iOS and Android.
- Remote config (for example Firebase Remote Config): changes without a deploy, with conditions by platform and audience.
Example config:
{
"ios": { "min_supported_version": "2.3.0", "latest_version": "2.5.1" },
"android": { "min_supported_version": "2.3.0", "latest_version": "2.5.1" }
}
Keep in mind:
- Compare versions part by part as numbers, not as strings:
2.10.0is newer than2.9.0. - Raise the minimum version only once the build is live in both stores; otherwise users see "please update" with no update available.
- If the config fails to load (no network), do not block the user; fall back to the last cached value.
- Also send the app version in a header with every request. The server can reply
426 Upgrade Requiredwhen a client is too old.
Android: Google Play In-App Updates
Google Play offers the In-App Updates API with two modes:
- IMMEDIATE: a full-screen flow where Play downloads, installs and restarts the app.
- FLEXIBLE: a background download while the user keeps working; once it finishes, you prompt for a restart.
val appUpdateManager = AppUpdateManagerFactory.create(context)
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
appUpdateManager.startUpdateFlowForResult(
info,
updateLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()
)
}
}
You can set an update priority when publishing through the Play Developer API, and clientVersionStalenessDays() tells you how long an update has been available. The API only works for installs from Google Play, so you still need your own min_supported_version check.
iOS: your own logic
iOS has no built-in equivalent of In-App Updates. The usual approach:
- Compare the current version with
min_supported_versionandlatest_versionfrom your config. - Show your own screen or dialog and open the app's App Store page.
- You can query the current App Store version via the iTunes Search API, but your own config is more reliable: you decide when a version becomes mandatory.
Remember phased release on the App Store: a new version reaches users gradually, though anyone can update manually right away.
API versioning for old clients
A forced update is a last resort. The main protection is an API that stays compatible with older versions:
- Add fields, but do not remove existing ones or change their meaning.
- Ship breaking changes in a new API version (
/v2/...) and keep the old one until its share of clients is acceptable. - Track the distribution of users across app versions; it drives the decision to raise the minimum.
- Make the client tolerate unknown fields and enum values.
UX for soft and hard updates
| Soft | Hard | |
|---|---|---|
| When | New features, fixes | Security issue, incompatible API, legal requirements |
| Form | Banner or dismissible dialog | Full-screen, no way around it |
| Frequency | Reasonable, remembering "later" | Every launch until updated |
| Copy | What is new and why update | Why the app cannot continue without updating |
Do not overuse hard updates: every blocking screen risks losing some users for good.