Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b1bda03
feat(ios): expo updates
hurali97 Apr 8, 2026
c726a34
feat(ios): expo updates working correctly
hurali97 Apr 8, 2026
ed0e8ae
feat(ios): patch expo updates
hurali97 Apr 9, 2026
48d50da
refactor(ios): cleanup
hurali97 Apr 9, 2026
fb2cefa
refactor(ios): cleanup
hurali97 Apr 9, 2026
a561efa
refactor(ios): cleanup
hurali97 Apr 9, 2026
c491d96
feat(android): add task dependency and asset source on expo updates
hurali97 Apr 13, 2026
d66535d
feat(android): add expo updates transitive deps - temporarily
hurali97 Apr 13, 2026
c3a87e1
feat(android): disable brownie temporarily
hurali97 Apr 13, 2026
373ea8d
feat(android): populate library manifest from app during CNG
hurali97 Apr 13, 2026
c2eb85e
chore(android): update expo patch 54
hurali97 Apr 14, 2026
8df5ce8
chore(android): add expo updates to 55 and remove brownie
hurali97 Apr 14, 2026
700fbe6
chore(android): add expo updates patch for 55
hurali97 Apr 14, 2026
d11c5b8
fix(android): duplicate libc++ on expo 55
hurali97 Apr 14, 2026
4ed5656
chore(android): update patch and expo config plugin for expo 54
hurali97 Apr 14, 2026
18e2774
chore: update patch for expo 54
hurali97 Apr 14, 2026
194f4f5
fix: main path
hurali97 Apr 14, 2026
67c0e53
fix: disable brownie temporarily
hurali97 Apr 14, 2026
1b2654b
refactor: cleanup
hurali97 Apr 15, 2026
da13b05
refactor: cleanup
hurali97 Apr 15, 2026
543bb5e
refactor: cleanup
hurali97 Apr 15, 2026
57f877f
refactor: cleanup
hurali97 Apr 15, 2026
5b217cb
refactor: use dynamic app name
hurali97 Apr 15, 2026
b0ff65e
refactor: cleanup
hurali97 Apr 15, 2026
8bd2cc5
refactor: cleanup
hurali97 Apr 15, 2026
ff11dd4
refactor: cleanup
hurali97 Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions .yarn/patches/expo-updates-npm-29.0.16-1c5c89eb83.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
diff --git a/android/src/main/java/expo/modules/updates/UpdatesController.kt b/android/src/main/java/expo/modules/updates/UpdatesController.kt
index 6ff694ccecafac0da2ffa2538500341e07e858d8..a670c0b833f33cf00981bc95daf44afa879cc69d 100644
--- a/android/src/main/java/expo/modules/updates/UpdatesController.kt
+++ b/android/src/main/java/expo/modules/updates/UpdatesController.kt
@@ -2,6 +2,7 @@ package expo.modules.updates

import android.content.Context
import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
import expo.modules.updates.events.IUpdatesEventManagerObserver
import expo.modules.updates.loader.LoaderTask
import expo.modules.updates.logging.UpdatesErrorCode
@@ -25,6 +26,19 @@ object UpdatesController {
@Volatile
private var overrideConfiguration: UpdatesConfiguration? = null

+ @Volatile
+ private var reactHost: ReactHost? = null
+
+ @JvmStatic
+ fun setReactHost(reactHost: ReactHost) {
+ this.reactHost = reactHost
+ }
+
+ @JvmStatic
+ fun getReactHost(): ReactHost? {
+ return reactHost
+ }
+
@JvmStatic
val instance: IUpdatesController
get() {
diff --git a/android/src/main/java/expo/modules/updates/procedures/RecreateReactContextProcedure.kt b/android/src/main/java/expo/modules/updates/procedures/RecreateReactContextProcedure.kt
index 3bf9f207ae7d3c46151103d8f809821a9df20d56..1ccb4877c2fcefd2c63d43dcc59664c83932805f 100644
--- a/android/src/main/java/expo/modules/updates/procedures/RecreateReactContextProcedure.kt
+++ b/android/src/main/java/expo/modules/updates/procedures/RecreateReactContextProcedure.kt
@@ -2,7 +2,6 @@ package expo.modules.updates.procedures

import android.app.Activity
import android.content.Context
-import com.facebook.react.ReactApplication
import expo.modules.updates.launcher.Launcher
import expo.modules.updates.statemachine.UpdatesStateEvent
import kotlinx.coroutines.CoroutineScope
@@ -20,16 +19,11 @@ class RecreateReactContextProcedure(
override val loggerTimerLabel = "timer-recreate-react-context"

override suspend fun run(procedureContext: ProcedureContext) {
- val reactApplication = context.applicationContext as? ReactApplication ?: run inner@{
- callback.onFailure(Exception("Could not reload application. Ensure you have passed the correct instance of ReactApplication into UpdatesController.initialize()."))
- return
- }
-
procedureContext.processStateEvent(UpdatesStateEvent.Restart())
callback.onSuccess()
procedureScope.launch {
withContext(Dispatchers.Main) {
- reactApplication.restart(weakActivity?.get(), "Restart from RecreateReactContextProcedure")
+ RestartReactAppExtensions.restart(weakActivity?.get(), "Restart from RecreateReactContextProcedure")
}
}
procedureContext.resetStateAfterRestart()
diff --git a/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt b/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt
index 64cc2870d28557c07217e1f78ad86e0fabe70bee..f2cf6a958d4da3f0a29040a6e101ce819c0696b7 100644
--- a/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt
+++ b/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt
@@ -43,11 +43,6 @@ class RelaunchProcedure(
override val loggerTimerLabel = "timer-relaunch"

override suspend fun run(procedureContext: ProcedureContext) {
- val reactApplication = context as? ReactApplication ?: run inner@{
- callback.onFailure(Exception("Could not reload application. Ensure you have passed the correct instance of ReactApplication into UpdatesController.initialize()."))
- return
- }
-
procedureContext.processStateEvent(UpdatesStateEvent.Restart())

val oldLaunchAssetFile = getCurrentLauncher().launchAssetFile
@@ -74,7 +69,7 @@ class RelaunchProcedure(
val newLaunchAssetFile = getCurrentLauncher().launchAssetFile
if (newLaunchAssetFile != null && newLaunchAssetFile != oldLaunchAssetFile) {
try {
- replaceLaunchAssetFileIfNeeded(reactApplication, newLaunchAssetFile)
+ replaceLaunchAssetFileIfNeeded(newLaunchAssetFile)
} catch (e: Exception) {
logger.error("Could not reset launchAssetFile for the ReactApplication", e, UpdatesErrorCode.Unknown)
}
@@ -84,7 +79,7 @@ class RelaunchProcedure(
procedureScope.launch {
withContext(Dispatchers.Main) {
reloadScreenManager?.show(weakActivity?.get())
- reactApplication.restart(weakActivity?.get(), "Restart from RelaunchProcedure")
+ RestartReactAppExtensions.restart(weakActivity?.get(), "Restart from RelaunchProcedure")
}
}

@@ -127,13 +122,16 @@ class RelaunchProcedure(
* [com.facebook.react.ReactInstanceManager].
*/
private fun replaceLaunchAssetFileIfNeeded(
- reactApplication: ReactApplication,
launchAssetFile: String
) {
if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
return
}

+ val reactApplication = context as? ReactApplication ?: run inner@{
+ callback.onFailure(Exception("Could not reload application. Ensure you have passed the correct instance of ReactApplication into UpdatesController.initialize()."))
+ return
+ }
val instanceManager = reactApplication.reactNativeHost.reactInstanceManager
val jsBundleLoaderField = instanceManager.javaClass.getDeclaredField("mBundleLoader")
jsBundleLoaderField.isAccessible = true
diff --git a/android/src/main/java/expo/modules/updates/procedures/RestartReactAppExtensions.kt b/android/src/main/java/expo/modules/updates/procedures/RestartReactAppExtensions.kt
index 25c6e804d31f9ee915acdf5f363af07e7b7bf424..3a90db88ac3e70fae3c6d909c955210093f306f9 100644
--- a/android/src/main/java/expo/modules/updates/procedures/RestartReactAppExtensions.kt
+++ b/android/src/main/java/expo/modules/updates/procedures/RestartReactAppExtensions.kt
@@ -2,25 +2,42 @@ package expo.modules.updates.procedures

import android.app.Activity
import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
+import com.facebook.react.ReactNativeHost
import com.facebook.react.common.LifecycleState
import expo.modules.rncompatibility.ReactNativeFeatureFlags
+import expo.modules.updates.UpdatesController

-/**
- * An extension for [ReactApplication] to restart the app
- *
- * @param activity For bridgeless mode if the ReactHost is destroyed, we need an Activity to resume it.
- * @param reason The restart reason. Only used on bridgeless mode.
- */
-internal fun ReactApplication.restart(activity: Activity?, reason: String) {
- if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
- val reactHost = this.reactHost
- check(reactHost != null)
- if (reactHost.lifecycleState != LifecycleState.RESUMED && activity != null) {
- reactHost.onHostResume(activity)
+private fun getReactHostReflectively(app: ReactApplication): ReactHost? {
+ return runCatching {
+ app.javaClass.getMethod("getReactHost").invoke(app) as? ReactHost
+ }.getOrNull()
+}
+
+private fun getReactNativeHostReflectively(app: ReactApplication): ReactNativeHost? {
+ return runCatching {
+ app.javaClass.getMethod("getReactNativeHost").invoke(app) as? ReactNativeHost
+ }.getOrNull()
+}
+
+object RestartReactAppExtensions {
+ /**
+ * A function to restart the app
+ *
+ * @param activity For bridgeless mode if the ReactHost is destroyed, we need an Activity to resume it.
+ * @param reason The restart reason. Only used on bridgeless mode.
+ */
+ fun restart(activity: Activity?, reason: String) {
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
+ val reactHost = UpdatesController.getReactHost() ?: getReactHostReflectively(activity?.application as ReactApplication)
+ check(reactHost != null)
+ if (reactHost.lifecycleState != LifecycleState.RESUMED && activity != null) {
+ reactHost.onHostResume(activity)
+ }
+ reactHost.reload(reason)
+ return
}
- reactHost.reload(reason)
- return
- }

- reactNativeHost.reactInstanceManager.recreateReactContextInBackground()
+ getReactNativeHostReflectively(activity?.application as ReactApplication)?.reactInstanceManager?.recreateReactContextInBackground()
+ }
}
diff --git a/ios/EXUpdates/UpdatesConfig.swift b/ios/EXUpdates/UpdatesConfig.swift
index 09a1e145a9f5a938e10dd3c59eedfd213ad54346..787c1a8f7ae6a010fff4200ffebe31742e9a25eb 100644
--- a/ios/EXUpdates/UpdatesConfig.swift
+++ b/ios/EXUpdates/UpdatesConfig.swift
@@ -141,7 +141,8 @@ public final class UpdatesConfig: NSObject {
}

private static func configDictionaryWithExpoPlist(mergingOtherDictionary: [String: Any]?) throws -> [String: Any] {
- guard let configPlistPath = Bundle.main.path(forResource: PlistName, ofType: "plist") else {
+ let bundle = Bundle(for: UpdatesConfig.self)
+ guard let configPlistPath = bundle.path(forResource: PlistName, ofType: "plist") else {
throw UpdatesConfigError.ExpoUpdatesConfigPlistError
}

diff --git a/ios/EXUpdates/UpdatesUtils.swift b/ios/EXUpdates/UpdatesUtils.swift
index a1e2c9af4ebaf9e50db2acaabfa876fc46d22454..0b96a7062dbcd021339a9daeb4397b3c7715b96d 100644
--- a/ios/EXUpdates/UpdatesUtils.swift
+++ b/ios/EXUpdates/UpdatesUtils.swift
@@ -108,18 +108,22 @@ public final class UpdatesUtils: NSObject {
return assetFilesMap
}

+ private static func getBundle() -> Bundle {
+ return Bundle(for: UpdatesUtils.self)
+ }
+
internal static func url(forBundledAsset asset: UpdateAsset) -> URL? {
guard let mainBundleDir = asset.mainBundleDir else {
- return Bundle.main.url(forResource: asset.mainBundleFilename, withExtension: asset.type)
+ return getBundle().url(forResource: asset.mainBundleFilename, withExtension: asset.type)
}
- return Bundle.main.url(forResource: asset.mainBundleFilename, withExtension: asset.type, subdirectory: mainBundleDir)
+ return getBundle().url(forResource: asset.mainBundleFilename, withExtension: asset.type, subdirectory: mainBundleDir)
}

internal static func path(forBundledAsset asset: UpdateAsset) -> String? {
guard let mainBundleDir = asset.mainBundleDir else {
- return Bundle.main.path(forResource: asset.mainBundleFilename, ofType: asset.type)
+ return getBundle().path(forResource: asset.mainBundleFilename, ofType: asset.type)
}
- return Bundle.main.path(forResource: asset.mainBundleFilename, ofType: asset.type, inDirectory: mainBundleDir)
+ return getBundle().path(forResource: asset.mainBundleFilename, ofType: asset.type, inDirectory: mainBundleDir)
}

/**
Loading
Loading