#Expo 55: Manual Setup
iOS
Supported out of the box from version [email protected] and onwards.
#Android
Android
This is a temporary patch until the Expo Team adds support and release a new version of Expo Updates.
#Step 1:
In expo-updates/android/build.gradle :
packagingOptions {
- pickFirst "**/libfbjni.so"
- pickFirst "**/libc++_shared.so"
-
resources.excludes.add("META-INF/LICENSE.md")
resources.excludes.add("META-INF/LICENSE-notice.md")
}#Step 2:
In expo-updates/android/**/UpdatesController.kt :
import android.content.Context
import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
import expo.modules.updates.events.IUpdatesEventManagerObserver
@@ -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
+ }
+#Step 3:
In expo-updates/android/**/UpdatesPackage.kt :
@@ -49,6 +49,10 @@ class UpdatesPackage : Package {
override fun onReactInstanceException(useDeveloperSupport: Boolean, exception: Exception) {
UpdatesController.instance.onReactInstanceException(exception)
}
+
+ override fun onDidCreateReactHost(context: Context, reactNativeHost: ReactHost) {
+ UpdatesController.setReactHost(reactNativeHost)
+ }
}
return listOf(handler)
}#Step 4:
In expo-updates/android/**/RecreateReactContextProcedure.kt :
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")
}
}#Step 5:
In expo-updates/android/**/RelaunchProcedure.kt :
import android.content.Context
-import com.facebook.react.ReactApplication
import expo.modules.updates.UpdatesConfiguration
import expo.modules.updates.db.DatabaseHolder
import expo.modules.updates.db.Reaper
@@ -40,11 +39,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 newLauncher = DatabaseLauncher(
@@ -71,7 +65,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")
}
}#Step 6:
In expo-updates/android/**/RestartReactAppExtensions.kt :
import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
import com.facebook.react.common.LifecycleState
+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) {
- 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()
+}
+
+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.
+ */
+ internal fun restart(activity: Activity?, reason: String) {
+ 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)
}
- reactHost.reload(reason)
}To see a complete patch, please visit:
