In-App Updates API
Supporting old versions of a live game can lead to server incompatibilities. The
Google.Play.AppUpdate package allows you to query the Play Store from within Unity to see if a newer version exists and prompt the user to download it.Installation
To add the plugin to your project, you should use the Google Play In-App Updates Unity repository. You can install it via the Unity Package Manager (UPM) or by importing the
.unitypackage. The package you specifically need is com.google.play.appupdate.Immediate vs Flexible Updates
The API supports two update flows:
- Immediate: A blocking, full-screen UI that requires the user to update and restart the app before they can continue. This is typically used for critical bug fixes.
- Flexible: A background download that lets the user keep interacting with the app. Once downloaded, you must prompt the user to install it by calling
- Immediate: A blocking, full-screen UI that requires the user to update and restart the app before they can continue. This is typically used for critical bug fixes.
- Flexible: A background download that lets the user keep interacting with the app. Once downloaded, you must prompt the user to install it by calling
CompleteUpdate(), which restarts the app.Implementing an Immediate Update
To trigger an update, instantiate an
AppUpdateManager, query the AppUpdateInfo, and check if the update type is allowed. If a user backgrounds the app during an Immediate update, you must resume the update when the app returns to the foreground by checking for DeveloperTriggeredUpdateInProgress.csharp
using Google.Play.AppUpdate;
using Google.Play.Common;
using UnityEngine;
using System.Collections;
public class ImmediateUpdateManager : MonoBehaviour {
private AppUpdateManager _appUpdateManager;
void Start() {
_appUpdateManager = new AppUpdateManager();
StartCoroutine(CheckForUpdate());
}
void OnApplicationPause(bool isPaused) {
if (!isPaused && _appUpdateManager != null) {
StartCoroutine(ResumeUpdateIfInProgress());
}
}
private IEnumerator CheckForUpdate() {
PlayAsyncOperation<AppUpdateInfo, AppUpdateErrorCode> appUpdateInfoOperation =
_appUpdateManager.GetAppUpdateInfo();
yield return appUpdateInfoOperation;
if (appUpdateInfoOperation.IsSuccessful) {
AppUpdateInfo appUpdateInfo = appUpdateInfoOperation.GetResult();
var updateOptions = AppUpdateOptions.ImmediateAppUpdateOptions();
if (appUpdateInfo.UpdateAvailability == UpdateAvailability.UpdateAvailable &&
appUpdateInfo.IsUpdateTypeAllowed(updateOptions)) {
StartCoroutine(StartImmediateUpdate(appUpdateInfo, updateOptions));
}
else if (appUpdateInfo.UpdateAvailability == UpdateAvailability.DeveloperTriggeredUpdateInProgress) {
// Resume the update if it was already in progress (e.g. app was backgrounded)
StartCoroutine(StartImmediateUpdate(appUpdateInfo, updateOptions));
}
}
}
private IEnumerator ResumeUpdateIfInProgress() {
var appUpdateInfoOperation = _appUpdateManager.GetAppUpdateInfo();
yield return appUpdateInfoOperation;
if (appUpdateInfoOperation.IsSuccessful) {
var appUpdateInfo = appUpdateInfoOperation.GetResult();
if (appUpdateInfo.UpdateAvailability == UpdateAvailability.DeveloperTriggeredUpdateInProgress) {
// Resume the update since it was already in progress
var updateOptions = AppUpdateOptions.ImmediateAppUpdateOptions();
StartCoroutine(StartImmediateUpdate(appUpdateInfo, updateOptions));
}
}
}
private IEnumerator StartImmediateUpdate(AppUpdateInfo info, AppUpdateOptions options) {
var startUpdateRequest = _appUpdateManager.StartUpdate(info, options);
yield return startUpdateRequest;
}
}Implementing a Flexible Update
Flexible updates allow users to continue playing while the update downloads. You must monitor the
AppUpdateRequest.Status. Once it reaches Downloaded, you call CompleteUpdate() to install it and restart the app.csharp
using Google.Play.AppUpdate;
using Google.Play.Common;
using UnityEngine;
using System.Collections;
public class FlexibleUpdateManager : MonoBehaviour {
private AppUpdateManager _appUpdateManager;
void Start() {
_appUpdateManager = new AppUpdateManager();
StartCoroutine(CheckForFlexibleUpdate());
}
private IEnumerator CheckForFlexibleUpdate() {
var appUpdateInfoOperation = _appUpdateManager.GetAppUpdateInfo();
yield return appUpdateInfoOperation;
if (appUpdateInfoOperation.IsSuccessful) {
var appUpdateInfo = appUpdateInfoOperation.GetResult();
var updateOptions = AppUpdateOptions.FlexibleAppUpdateOptions();
if (appUpdateInfo.UpdateAvailability == UpdateAvailability.UpdateAvailable &&
appUpdateInfo.IsUpdateTypeAllowed(updateOptions)) {
StartCoroutine(MonitorFlexibleUpdate(appUpdateInfo, updateOptions));
}
}
}
private IEnumerator MonitorFlexibleUpdate(AppUpdateInfo info, AppUpdateOptions options) {
var startUpdateRequest = _appUpdateManager.StartUpdate(info, options);
// Track the update state using the supported operation approach
while (!startUpdateRequest.IsDone) {
Debug.Log($"Download Progress: {startUpdateRequest.DownloadProgress}");
yield return null;
}
if (startUpdateRequest.Status == AppUpdateStatus.Downloaded) {
// Display a prompt to the user to install the update.
// Do NOT call CompleteUpdate() automatically here.
ShowInstallPromptToUser();
}
}
// Call this when the user clicks your install button
public void OnUserClickedInstall() {
_appUpdateManager.CompleteUpdate();
}
private void ShowInstallPromptToUser() {
// Implement your UI logic here
}
}Testing the Integration
You cannot test In-App Updates by simply building an APK and pushing it to your device over USB via the Unity Editor. The API checks the Play Store directly. You must use Internal App Sharing or a closed testing track.
Ensure the following:
- Your test device must have the Google Play Store app installed.
- You must be logged into a Google account that has access to the Internal App Sharing track.
- The base app installed on your device must share the exact same Application ID and signing key as the Google Play version.
- To simulate an actual update, the test version installed on your device must have a lower
- Note that the Play Store caches app update statuses. If the prompt does not appear, clear the cache of the Google Play Store app on your test device.
Ensure the following:
- Your test device must have the Google Play Store app installed.
- You must be logged into a Google account that has access to the Internal App Sharing track.
- The base app installed on your device must share the exact same Application ID and signing key as the Google Play version.
- To simulate an actual update, the test version installed on your device must have a lower
versionCode than the one published on Internal App Sharing. However, note that Internal App Sharing itself does not strictly enforce unique or increasing version codes for uploads.- Note that the Play Store caches app update statuses. If the prompt does not appear, clear the cache of the Google Play Store app on your test device.
warningIn-App Updates rely entirely on the Google Play Store app. They cannot be tested within the Unity Editor or on devices lacking Google Play Services.
Frequently Asked Questions
Why isn't the update prompt appearing during testing?
This is the most common issue. Double-check that your device has the base APK installed and that you uploaded an update with a higher
What happens if a user interrupts an Immediate update?
If the user closes the app or navigates away, the update will halt. When they return to the app,
Can I force an update if a user ignores a Flexible update?
Yes, you can define your own logic. For example, you can implement a server-side version check using Firebase Remote Config. If their version falls too far behind, you can block access to multiplayer features and trigger an Immediate update instead of a Flexible one.
This is the most common issue. Double-check that your device has the base APK installed and that you uploaded an update with a higher
versionCode to Internal App Sharing. If everything matches, clear the cache and storage of the Google Play Store app on your device, as it aggressively caches update availability.What happens if a user interrupts an Immediate update?
If the user closes the app or navigates away, the update will halt. When they return to the app,
GetAppUpdateInfo() will return UpdateAvailability.DeveloperTriggeredUpdateInProgress. You must immediately resume the update using the snippet provided above.Can I force an update if a user ignores a Flexible update?
Yes, you can define your own logic. For example, you can implement a server-side version check using Firebase Remote Config. If their version falls too far behind, you can block access to multiplayer features and trigger an Immediate update instead of a Flexible one.
