Downloading updates
Edit this page
Learn strategies for downloading and launching updates.
All of the following information on this page applies only to release builds and debug builds withEX_UPDATES_NATIVE_DEBUG
enabled.
In this section, we'll cover the different strategies for downloading and launching updates. The goal is to ensure that the end user adopts the latest version of the app as soon as possible after it is published, without sacrificing the user experience by introducing slow loading screens or other issues. The strategies are not mutually exclusive, and you can mix and match them as needed for the requirements of your app.
Updates are loaded asynchronously on startup by default
The default behavior is to check for an update when the app is cold booted (launched from a killed state) and download the update if it's available. This process does not block loading the app, so when using this strategy the end user will only load the update when they cold boot the app after an update has been published, and then at some point kill and restart the app (for example, if they close it from the recent apps list on the OS or if they turn the device off and on).
This behavior is safe because it doesn't interfere with app startup to wait for a network request to complete (which would be a bad user experience in common real-world cases where a user finds themself with a slow connection and they are stuck on a loading screen for several seconds). The downside is that it takes much longer for users to adopt the latest version of the app. If the ideal is for an update to be adopted immediately by all users as soon as it is published, then this strategy falls very short of that.
What if I want to always block app startup until the latest update is downloaded?
We recommend against this strategy because the resulting user experience is extremely poor. Typically when a user is stuck waiting on a splash screen when booting an app, they will close the app and try again (and so downloading the update won't complete), or give up and use another app. When the users' device is connected to a slow network, even when there is no update, they may have to wait several seconds or more to load the app. If ensuring that your users always have the latest version of your app is critical, you may want to explore one of the other strategies explained here.
How can I disable the default behavior?
You can disable the default behavior by setting the checkAutomatically
option to NEVER
in the updates
configuration. This will prevent the app from checking for updates and downloading them automatically.
Checking for updates while the app is running
You can use Updates.checkForUpdateAsync()
to check for updates while the app is running. This will return a promise that resolves to a UpdateCheckResult
object, with isAvailable
set to true
if an update is available, and information about the update in the manifest
property.
If an update is available, you can use the Updates.downloadAsync()
method to download the update. This will return a promise that resolves when the download is complete. Finally, you can use the Updates.reloadAsync()
method to reload the app with the new version. The useUpdates()
hook can also be used to monitor the state of the expo-updates
library from a React component.
What are common patterns for checking for updates while the app is running?
- You can check for updates at various points in your app's lifecycle, such as when it is foregrounded or at some interval. When an update is found, you may want to show a dialog to the user to prompt the user to update.
- You can check for updates at launch and display your own custom loading screen, if it is very important for your use case to ensure that users always get the latest version at launch.
Checking for updates while the app is backgrounded
You can use expo-background-task
to check for updates while the app is backgrounded. To do this, use the same Updates.checkForUpdateAsync()
and Updates.downloadAsync()
methods as you would in the foreground, but execute them inside of a background task. This is a great way to ensure that the user always has the latest version of the app, even if they have not opened the app in a while.
It's worth considering whether you want to reload the app after an update is downloaded in the background, or wait for the user to close and reopen it. If you choose to only download it in the background and not apply it, this should still be useful to ensure that the next boot will immediately have the latest version, and it will lead to a faster adoption rate for updates compared to the default behavior.
An example of how to check for updates in the background
To ensure the background task is registered when the application starts, import and invoke the setupBackgroundUpdates
function within the top-level component.
import * as TaskManager from 'expo-task-manager';
import * as BackgroundTask from 'expo-background-task';
import * as Updates from 'expo-updates';
export const setupBackgroundUpdates = async () => {
const BACKGROUND_TASK_NAME = 'task-run-expo-update';
// Define the background task - this must be done each time the app starts
TaskManager.defineTask(BACKGROUND_TASK_NAME, async () => {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
return Promise.resolve();
});
// Register the task if it was not previously registered
const isRegistered = TaskManager.isTaskRegisteredAsync(BACKGROUND_TASK_NAME);
if (!isRegistered) {
await BackgroundTask.registerTaskAsync(BACKGROUND_TASK_NAME, {
minimumInterval: 60 * 24, // 24 hours
});
}
};
Should I also apply the update with Updates.reloadAsync() when the app is backgrounded?
Support for calling Updates.reloadAsync()
while the app is backgrounded is experimental. This is a new feature and it is not widely used, be sure to monitor for crashes when you first enable it. Downloading an update in the background is safe.
Reloading an update while the app is backgrounded can be a great way to ensure that the user has the latest version of the app when they open it again. However, it is important to note that, unless you persist the state that the app was in at the time it was backgrounded and restore that state, the user will experience a cold boot when they open the app again. One way to mitigate this is to only do a reload in the background if the app has been inactive for a certain period of time, after which a user is unlikely to expect the app to restore its previous state.
Critical/mandatory updates
There is no first-class support for critical/mandatory updates in the expo-updates
library. However, you can implement your own logic to check for critical updates and apply them manually. The expo/UpdatesAPIDemo
repository contains an example of one way to approach this. You can combine that approach with the strategies above to check for updates.
Controlling which update to load from the client side
The typical way to use EAS Update is to have a single update URL and a set of request headers (such as update channel name) embedded in a build of your app. To control which update is loaded, you make changes on the server through the eas update
command or the EAS dashboard. For example, you publish a new update to a channel that your build is pointing to, then the build fetches that update on the next launch. Updates published to a channel different from the one your build is pointing to will not be downloaded with this approach.
You can override the update URL and request headers at runtime using the Updates.setUpdateURLAndRequestHeadersOverride()
method. This can be useful if you want to load a specific update or change the update channel while the app is running. Learn more.
Monitoring adoption of updates
The details page for an update (for example: https://expo.dev/accounts/[account]/projects/[project]/updates/[id]
) shows metrics for the number of users who have run the update, in addition to the number of failed installs (users who download and attempted to run the update, but it crashed).
The deployments page (for example: https://expo.dev/accounts/[account]/projects/[project]/deployments/production/[runtime-version]
) includes a table and chart that shows the number of users who have run each update related to a particular update channel and runtime version combination, over a given time period.