View file-by-file diffs of all the changes you need to make to your native projects to upgrade them to the next Expo SDK version.
If you manage your native projects (previously known as bare workflow), to upgrade to the latest Expo SDK, you have to make changes to your native projects. It can be a complex process to find which native file changes and what to update in which file.
The following guide provides diffs to compare native project files between your project's current SDK version and the target SDK version you want to upgrade. You can use them to make changes to your project depending on the expo
package version your project uses. The tools on this page are similar to React Native Upgrade Helper. However, they are oriented around projects that use Expo modules and related tooling.
Interested in avoiding upgrading native code altogether? See Continuous Native Generation (CNG) to learn how Expo Prebuild can generate your native projects before a build.
Once you have upgraded your Expo SDK version and related dependencies, use the diff tool below to learn about changes you need to make to your native project and bring them up to date with the current Expo SDK version.
Choose your from SDK version and a to SDK version to see the diff. Then, apply those changes to your native projects by copying and pasting or manually making changes to the project files.
MODIFIED
22 | 22 | android/app/build |
23 | 23 | android/.gradle |
24 | 24 | ios/.xcode.env.local |
25 | ||
26 | # Exclude tarballs generated by `npm pack` | |
27 | /*.tgz |
MODIFIED
90 | 90 | targetSdkVersion rootProject.ext.targetSdkVersion |
91 | 91 | versionCode 1 |
92 | 92 | versionName "1.0" |
93 | ||
94 | buildConfigField("boolean", "REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS", (findProperty("reactNative.unstable_useRuntimeSchedulerAlways") ?: true).toString()) | |
95 | 93 | } |
96 | 94 | signingConfigs { |
97 | 95 | debug { |
163 | 161 | } |
164 | 162 | } |
165 | 163 | |
166 | implementation("com.facebook.react:flipper-integration") | |
167 | ||
168 | 164 | if (hermesEnabled.toBoolean()) { |
169 | 165 | implementation("com.facebook.react:hermes-android") |
170 | 166 | } else { |
MODIFIED
19 | 19 | </queries> |
20 | 20 | |
21 | 21 | <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> |
22 | <meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="YOUR-APP-URL-HERE"/> | |
23 | <meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="YOUR-APP-SDK-VERSION-HERE"/> | |
24 | 22 | <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true"> |
25 | 23 | <intent-filter> |
26 | 24 | <action android:name="android.intent.action.MAIN"/> |
29 | 27 | </activity> |
30 | 28 | <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/> |
31 | 29 | </application> |
32 | </manifest> | |
30 | </manifest> |
MODIFIED
2 | 2 | |
3 | 3 | import android.app.Application |
4 | 4 | import android.content.res.Configuration |
5 | import androidx.annotation.NonNull | |
6 | 5 | |
7 | 6 | import com.facebook.react.PackageList |
8 | 7 | import com.facebook.react.ReactApplication |
9 | 8 | import com.facebook.react.ReactNativeHost |
10 | 9 | import com.facebook.react.ReactPackage |
11 | 10 | import com.facebook.react.ReactHost |
12 | import com.facebook.react.config.ReactFeatureFlags | |
13 | 11 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load |
14 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost | |
15 | 12 | import com.facebook.react.defaults.DefaultReactNativeHost |
16 | import com.facebook.react.flipper.ReactNativeFlipper | |
17 | 13 | import com.facebook.soloader.SoLoader |
18 | 14 | |
19 | 15 | import expo.modules.ApplicationLifecycleDispatcher |
40 | 36 | ) |
41 | 37 | |
42 | 38 | override val reactHost: ReactHost |
43 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost) | |
39 | get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) | |
44 | 40 | |
45 | 41 | override fun onCreate() { |
46 | 42 | super.onCreate() |
47 | 43 | SoLoader.init(this, false) |
48 | if (!BuildConfig.REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS) { | |
49 | ReactFeatureFlags.unstable_useRuntimeSchedulerAlways = false | |
50 | } | |
51 | 44 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { |
52 | 45 | // If you opted-in for the New Architecture, we load the native entry point for this app. |
53 | 46 | load() |
54 | 47 | } |
55 | if (BuildConfig.DEBUG) { | |
56 | ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager) | |
57 | } | |
58 | 48 | ApplicationLifecycleDispatcher.onApplicationCreate(this) |
59 | 49 | } |
60 | 50 |
MODIFIED
17 | 17 | android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material" |
18 | 18 | android:insetRight="@dimen/abc_edit_text_inset_horizontal_material" |
19 | 19 | android:insetTop="@dimen/abc_edit_text_inset_top_material" |
20 | android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"> | |
20 | android:insetBottom="@dimen/abc_edit_text_inset_bottom_material" | |
21 | > | |
21 | 22 | |
22 | 23 | <selector> |
23 | 24 | <!-- |
MODIFIED
6 | 6 | minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23') |
7 | 7 | compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34') |
8 | 8 | targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') |
9 | kotlinVersion = findProperty('android.kotlinVersion') ?: '1.8.10' | |
9 | kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.23' | |
10 | 10 | |
11 | ndkVersion = "25.1.8937393" | |
11 | ndkVersion = "26.1.10909125" | |
12 | 12 | } |
13 | 13 | repositories { |
14 | 14 | google() |
17 | 17 | dependencies { |
18 | 18 | classpath('com.android.tools.build:gradle') |
19 | 19 | classpath('com.facebook.react:react-native-gradle-plugin') |
20 | classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') | |
20 | 21 | } |
21 | 22 | } |
22 | 23 |
MODIFIED
10 | 10 | local.properties |
11 | 11 | *.iml |
12 | 12 | *.hprof |
13 | .cxx/ | |
13 | 14 | |
14 | 15 | # Bundle artifacts |
15 | 16 | *.jsbundle |
MODIFIED
1 | 1 | distributionBase=GRADLE_USER_HOME |
2 | 2 | distributionPath=wrapper/dists |
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip | |
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip | |
4 | 4 | networkTimeout=10000 |
5 | 5 | validateDistributionUrl=true |
6 | 6 | zipStoreBase=GRADLE_USER_HOME |
MODIFIED
26 | 26 | |
27 | 27 | set DIRNAME=%~dp0 |
28 | 28 | if "%DIRNAME%"=="" set DIRNAME=. |
29 | @rem This is normally unused | |
29 | 30 | set APP_BASE_NAME=%~n0 |
30 | 31 | set APP_HOME=%DIRNAME% |
31 | 32 | |
42 | 43 | %JAVA_EXE% -version >NUL 2>&1 |
43 | 44 | if %ERRORLEVEL% equ 0 goto execute |
44 | 45 | |
45 | echo. | |
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |
47 | echo. | |
48 | echo Please set the JAVA_HOME variable in your environment to match the | |
49 | echo location of your Java installation. | |
46 | echo. 1>&2 | |
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 | |
48 | echo. 1>&2 | |
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 | |
50 | echo location of your Java installation. 1>&2 | |
50 | 51 | |
51 | 52 | goto fail |
52 | 53 | |
56 | 57 | |
57 | 58 | if exist "%JAVA_EXE%" goto execute |
58 | 59 | |
59 | echo. | |
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | |
61 | echo. | |
62 | echo Please set the JAVA_HOME variable in your environment to match the | |
63 | echo location of your Java installation. | |
60 | echo. 1>&2 | |
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 | |
62 | echo. 1>&2 | |
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 | |
64 | echo location of your Java installation. 1>&2 | |
64 | 65 | |
65 | 66 | goto fail |
66 | 67 |
MODIFIED
2 | 2 | "expo": { |
3 | 3 | "name": "HelloWorld", |
4 | 4 | "slug": "expo-template-bare", |
5 | "version": "1.0.0", | |
6 | "assetBundlePatterns": ["**/*"] | |
5 | "version": "1.0.0" | |
7 | 6 | } |
8 | 7 | } |
MODIFIED
212 | 212 | ); |
213 | 213 | runOnlyForDeploymentPostprocessing = 0; |
214 | 214 | shellPath = /bin/sh; |
215 | shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios relative | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; | |
215 | shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; | |
216 | 216 | }; |
217 | 217 | 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { |
218 | 218 | isa = PBXShellScriptBuildPhase; |
MODIFIED
18 | 18 | |
19 | 19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge |
20 | 20 | { |
21 | return [self getBundleURL]; | |
21 | return [self bundleURL]; | |
22 | 22 | } |
23 | 23 | |
24 | - (NSURL *)getBundleURL | |
24 | - (NSURL *)bundleURL | |
25 | 25 | { |
26 | 26 | #if DEBUG |
27 | 27 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; |
MODIFIED
35 | 35 | <string>SplashScreen</string> |
36 | 36 | <key>UIRequiredDeviceCapabilities</key> |
37 | 37 | <array> |
38 | <string>armv7</string> | |
38 | <string>arm64</string> | |
39 | 39 | </array> |
40 | 40 | <key>UIStatusBarStyle</key> |
41 | 41 | <string>UIStatusBarStyleDefault</string> |
MODIFIED
2 | 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
3 | 3 | <plist version="1.0"> |
4 | 4 | <dict> |
5 | <key>EXUpdatesSDKVersion</key> | |
6 | <string>YOUR-APP-SDK-VERSION-HERE</string> | |
7 | <key>EXUpdatesURL</key> | |
8 | <string>YOUR-APP-URL-HERE</string> | |
9 | 5 | </dict> |
10 | 6 | </plist> |
MODIFIED
13 | 13 | |
14 | 14 | prepare_react_native_project! |
15 | 15 | |
16 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. | |
17 | # because `react-native-flipper` depends on (FlipperKit,...), which will be excluded. To fix this, | |
18 | # you can also exclude `react-native-flipper` in `react-native.config.js` | |
19 | # | |
20 | # ```js | |
21 | # module.exports = { | |
22 | # dependencies: { | |
23 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), | |
24 | # } | |
25 | # } | |
26 | # ``` | |
27 | flipper_config = FlipperConfiguration.disabled | |
28 | if ENV['NO_FLIPPER'] == '1' then | |
29 | # Explicitly disabled through environment variables | |
30 | flipper_config = FlipperConfiguration.disabled | |
31 | elsif podfile_properties.key?('ios.flipper') then | |
32 | # Configure Flipper in Podfile.properties.json | |
33 | if podfile_properties['ios.flipper'] == 'true' then | |
34 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"]) | |
35 | elsif podfile_properties['ios.flipper'] != 'false' then | |
36 | flipper_config = FlipperConfiguration.enabled(["Debug", "Release"], { 'Flipper' => podfile_properties['ios.flipper'] }) | |
37 | end | |
38 | end | |
39 | ||
40 | 16 | target 'HelloWorld' do |
41 | 17 | use_expo_modules! |
42 | 18 | config = use_native_modules! |
49 | 25 | :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', |
50 | 26 | # An absolute path to your application root. |
51 | 27 | :app_path => "#{Pod::Config.instance.installation_root}/..", |
52 | # Note that if you have use_frameworks! enabled, Flipper will not work if enabled | |
53 | :flipper_configuration => flipper_config | |
54 | 28 | ) |
55 | 29 | |
56 | 30 | post_install do |installer| |
MODIFIED
1 | 1 | { |
2 | 2 | "name": "expo-template-bare-minimum", |
3 | 3 | "description": "This bare project template includes a minimal setup for using unimodules with React Native.", |
4 | "version": "50.0.42", | |
4 | "version": "51.0.2", | |
5 | 5 | "main": "index.js", |
6 | 6 | "scripts": { |
7 | 7 | "start": "expo start --dev-client", |
10 | 10 | "web": "expo start --web" |
11 | 11 | }, |
12 | 12 | "dependencies": { |
13 | "expo": "~50.0.14", | |
14 | "expo-status-bar": "~1.11.1", | |
13 | "expo": "~51.0.0-preview.0", | |
14 | "expo-status-bar": "~1.12.0", | |
15 | 15 | "react": "18.2.0", |
16 | "react-native": "0.73.6" | |
16 | "react-native": "0.74.0" | |
17 | 17 | }, |
18 | 18 | "devDependencies": { |
19 | 19 | "@babel/core": "^7.20.0" |