--- title: Add expo-updates to an existing project description: Learn how to add expo-updates to an existing React Native project. --- import { DiffBlock, Terminal } from '~/ui/components/Snippet'; import { Collapsible } from '~/ui/components/Collapsible'; The `expo-updates` library fetches and manages updates received from a remote server. It supports [EAS Update](/eas-update/introduction), a hosted service that serves updates for projects using the expo-updates library. > If you are creating a new project, we recommend using `npx create-react-native-app` instead of `npx react-native init` because it will handle the following configuration for you automatically. It includes the `expo-updates` [config plugin](/guides/config-plugins), which will handle the following steps for you. ## Installation The `expo-updates` library requires that your project already has [Expo modules configured](/bare/installing-expo-modules). Be sure to install it before continuing. To get started, install `expo-updates`: Then, install pods: Once installation is complete, apply the changes from the following diffs to configure `expo-updates` in your project. ## Configuration in JavaScript and JSON You'll need to modify **index.js** to import `expo-asset` early in your app, to be able to update assets with updates. ```diff index.js + import 'expo-asset'; import { registerRootComponent } from 'expo'; import App from './App'; // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in Expo Go or in a native build, // the environment is set up appropriately registerRootComponent(App); ``` ## Configuration for iOS Add the **Supporting** directory containing **Expo.plist** to your project in Xcode with the following content: ```xml Expo.plist EXUpdatesCheckOnLaunch ALWAYS EXUpdatesEnabled EXUpdatesLaunchWaitMs 0 EXUpdatesSDKVersion 46.0.0 EXUpdatesURL https://exp.host/@my-expo-username/my-app ``` ## Configuration for Android Apply the following configurations to your **AndroidManifest.xml** file: ```diff AndroidManifest.xml + + + + + + ``` ## Customizing Automatic Setup for iOS By default, `expo-updates` requires no additional setup. If you want to customize the installation, for example, to enable updates only in some build variants, you can instead follow these manual setup steps and then apply any customizations. ### Update AppDelegate.h ```diff apps/bare-update/ios/bareupdate/AppDelegate.h #import + #import #import #import #import - @interface AppDelegate : EXAppDelegateWrapper + @interface AppDelegate : EXAppDelegateWrapper ``` ### Update AppDelegate.mm There are multiple changes to apply to your project's **AppDelegate.mm**. ```diff apps/bare-update/ios/bareupdate/AppDelegate.mm + @interface AppDelegate () + + @property (nonatomic, strong) NSDictionary *launchOptions; + + @end + @implementation AppDelegate ``` ```diff apps/bare-update/ios/bareupdate/AppDelegate.mm - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; + self.launchOptions = launchOptions; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + #ifdef DEBUG + [self initializeReactNativeApp]; + #else + EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance]; + controller.delegate = self; + [controller startAndShowLaunchScreen:self.window]; + #endif + + [super application:application didFinishLaunchingWithOptions:launchOptions]; + + return YES; + } + + - (RCTBridge *)initializeReactNativeApp + { + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil]; id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"]; if (rootViewBackgroundColor != nil) { ``` ```diff apps/bare-update/ios/bareupdate/AppDelegate.mm rootView.backgroundColor = [UIColor whiteColor]; } - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; - [super application:application didFinishLaunchingWithOptions:launchOptions]; - - return YES; + return bridge; } - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge ``` ```diff apps/bare-update/ios/bareupdate/AppDelegate.mm #ifdef DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + return [[EXUpdatesAppController sharedInstance] launchAssetUrl]; #endif } + - (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success { + appController.bridge = [self initializeReactNativeApp]; + } // Linking API - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { return [RCTLinkingManager application:application openURL:url options:options]; ``` ### Expo.plist ```diff apps/bare-update/ios/bareupdate/Supporting/Expo.plist EXUpdatesURL https://exp.host/@my-expo-username/my-app + EXUpdatesAutoSetup + ``` ## Customizing Automatic Setup for Android By default, `expo-updates` requires no additional setup. If you want to customize the installation, for example, to enable updates only in some build variants, you can instead follow these manual setup steps and then apply any customizations. ### AndroidManifest.xml ```diff apps/bare-update/android/app/src/main/AndroidManifest.xml + ``` ### MainApplication.java There are multiple changes to apply to your project's **MainApplication.java** ```diff apps/bare-update/android/app/src/main/java/com/bareupdate/MainApplication.java import android.app.Application; import android.content.Context; import android.content.res.Configuration; + import android.net.Uri; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; ``` ```diff apps/bare-update/android/app/src/main/java/com/bareupdate/MainApplication.java import expo.modules.ApplicationLifecycleDispatcher; import expo.modules.ReactNativeHostWrapper; + import expo.modules.updates.UpdatesController; import com.facebook.react.bridge.JSIModulePackage; import com.swmansion.reanimated.ReanimatedJSIModulePackage; ``` ```diff apps/bare-update/android/app/src/main/java/com/bareupdate/MainApplication.java import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; + import javax.annotation.Nullable; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper( ``` ```diff apps/bare-update/android/app/src/main/java/com/bareupdate/MainApplication.java protected JSIModulePackage getJSIModulePackage() { return new ReanimatedJSIModulePackage(); } + + @Override + protected @Nullable String getJSBundleFile() { + if (BuildConfig.DEBUG) { + return super.getJSBundleFile(); + } else { + return UpdatesController.getInstance().getLaunchAssetFile(); + } + } + + @Override + protected @Nullable String getBundleAssetName() { + if (BuildConfig.DEBUG) { + return super.getBundleAssetName(); + } else { + return UpdatesController.getInstance().getBundleAssetName(); + } + } }); ``` ```diff apps/bare-update/android/app/src/main/java/com/bareupdate/MainApplication.java super.onCreate(); SoLoader.init(this, /* native exopackage */ false); + if (!BuildConfig.DEBUG) { + UpdatesController.initialize(this); + } + initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); ApplicationLifecycleDispatcher.onApplicationCreate(this); } ``` ## Usage See more information about usage in the [`expo-updates` README](https://github.com/expo/expo/tree/main/packages/expo-updates/README.md). ## FAQ If you have assets (such as images or other media) that are imported in your application code, and you would like these to be downloaded atomically as part of an update, add the `assetBundlePatterns` field under the `expo` key in your project's **app.json**. This field should be an array of file glob strings that point to the assets you want to be bundled. For example: `"assetBundlePatterns": ["**/*"]`