Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bridgeless Mode support (New Architecture) [0.74] #1769

Open
angelo-hub opened this issue Apr 11, 2024 · 17 comments
Open

Bridgeless Mode support (New Architecture) [0.74] #1769

angelo-hub opened this issue Apr 11, 2024 · 17 comments

Comments

@angelo-hub
Copy link

Bridgeless Mode is going to be default in 0.74 can offer some bandwidth to migrate to bridgeless mode as this is a super cool project

@enahum
Copy link

enahum commented May 20, 2024

Have you been able to make it work even with the bridge on 74? On Android there is no longer getJSIModulePackage I cannot seem to find a solution to this yet.

@enahum
Copy link

enahum commented May 20, 2024

@radex do you think you can take a look at this to solve the breaking change introduced by RN 74 ?

@karengharibyan
Copy link

@hawadlu
Copy link

hawadlu commented May 31, 2024

@angelo-hub Any progress on this one?

@yuyongmao
Copy link

Have you been able to make it work even with the bridge on 74? On Android there is no longer getJSIModulePackage I cannot seem to find a solution to this yet.

@enahum Is there any progress on this issue? I have the same problem.

@suman379
Copy link

@radex any plan for new arch?

@LukasMod
Copy link

@radex Can we get an answer in this thread?
Are there any plans to restore the compatibility of the library with the latest version of react native (current 0.74.3)?

@radex
Copy link
Collaborator

radex commented Jul 24, 2024

Yes, sorry for staying quiet about it. Yes, I'm planning to work on this likely in August.

@3210jr
Copy link

3210jr commented Aug 9, 2024

Hey @radex - I am happy to help with chores and low hanging fruit re new architecture support. Not my area of expertise but if there is a pull request I will contribute

@lucaswitch
Copy link

Hey @radex - I am happy to help with chores and low hanging fruit re new architecture support. Not my area of expertise but if there is a pull request I will contribute

I'm happy to help too

@juliesaia-vendora
Copy link

I got it working on 0.74.5 (with bridge) with this patch, it reverts the deprecation and removes BridgelessCatalystInstance.kt.

It might be possible to do the same with 0.75, but it will be more difficult because there they actually removed the JSIModule stuff. I might try it later though

Be sure to enable building from source on Android (android/settings.gradle):

includeBuild('../node_modules/react-native') {
    dependencySubstitution {
        substitute(module("com.facebook.react:react-android")).using(project(":packages:react-native:ReactAndroid"))
        substitute(module("com.facebook.react:react-native")).using(project(":packages:react-native:ReactAndroid"))
        substitute(module("com.facebook.react:hermes-android")).using(project(":packages:react-native:ReactAndroid:hermes-engine"))
        substitute(module("com.facebook.react:hermes-engine")).using(project(":packages:react-native:ReactAndroid:hermes-engine"))
    }
}

The patch:

diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
index 7258ab9..6df97c6 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
@@ -55,6 +55,8 @@ import com.facebook.react.bridge.CatalystInstance;
 import com.facebook.react.bridge.CatalystInstanceImpl;
 import com.facebook.react.bridge.JSBundleLoader;
 import com.facebook.react.bridge.JSExceptionHandler;
+import com.facebook.react.bridge.JSIModulePackage;
+import com.facebook.react.bridge.JSIModuleType;
 import com.facebook.react.bridge.JavaJSExecutor;
 import com.facebook.react.bridge.JavaScriptExecutor;
 import com.facebook.react.bridge.JavaScriptExecutorFactory;
@@ -184,6 +186,7 @@ public class ReactInstanceManager {
   private volatile Boolean mHasStartedDestroying = false;
   private final MemoryPressureRouter mMemoryPressureRouter;
   private final @Nullable JSExceptionHandler mJSExceptionHandler;
+    private final @Nullable JSIModulePackage mJSIModulePackage;
   private final @Nullable UIManagerProvider mUIManagerProvider;
   private final @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder;
   private List<ViewManager> mViewManagers;
@@ -232,6 +235,7 @@ public class ReactInstanceManager {
       @Nullable DevBundleDownloadListener devBundleDownloadListener,
       int minNumShakes,
       int minTimeLeftInFrameForNonBatchedOperationMs,
+      @Nullable JSIModulePackage jsiModulePackage,
       @Nullable UIManagerProvider uIManagerProvider,
       @Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
       @Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder,
@@ -292,6 +296,7 @@ public class ReactInstanceManager {
       }
       mPackages.addAll(packages);
     }
+    mJSIModulePackage = jsiModulePackage;
     mUIManagerProvider = uIManagerProvider;
 
     // Instantiate ReactChoreographer in UI thread.
@@ -1399,15 +1404,20 @@ public class ReactInstanceManager {
       }
     }
 
+    if (mJSIModulePackage != null) {
+      catalystInstance.addJSIModules(
+          mJSIModulePackage.getJSIModules(
+              reactContext, catalystInstance.getJavaScriptContextHolder()));
+    }
+
     if (mUIManagerProvider != null) {
       UIManager uiManager = mUIManagerProvider.createUIManager(reactContext);
       if (uiManager != null) {
-        catalystInstance.setFabricUIManager(uiManager);
-
-        // Initialize the UIManager
         uiManager.initialize();
         catalystInstance.setFabricUIManager(uiManager);
       }
+    } else if (ReactFeatureFlags.enableFabricRenderer) {
+    catalystInstance.getJSIModule(JSIModuleType.UIManager);
     }
     if (mBridgeIdleDebugListener != null) {
       catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
index d467ab9..3ae051f 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java
@@ -20,6 +20,7 @@ import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
 import com.facebook.infer.annotation.Assertions;
 import com.facebook.react.bridge.JSBundleLoader;
 import com.facebook.react.bridge.JSExceptionHandler;
+import com.facebook.react.bridge.JSIModulePackage;
 import com.facebook.react.bridge.JavaScriptExecutorFactory;
 import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
 import com.facebook.react.bridge.UIManagerProvider;
@@ -67,6 +68,7 @@ public class ReactInstanceManagerBuilder {
   private @Nullable JavaScriptExecutorFactory mJavaScriptExecutorFactory;
   private int mMinNumShakes = 1;
   private int mMinTimeLeftInFrameForNonBatchedOperationMs = -1;
+  private @Nullable JSIModulePackage mJSIModulesPackage;
   private @Nullable UIManagerProvider mUIManagerProvider;
   private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;
   private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder;
@@ -77,6 +79,12 @@ public class ReactInstanceManagerBuilder {
 
   /* package protected */ ReactInstanceManagerBuilder() {}
 
+  public ReactInstanceManagerBuilder setJSIModulesPackage(
+      @Nullable JSIModulePackage jsiModulePackage) {
+    mJSIModulesPackage = jsiModulePackage;
+    return this;
+  }
+
   /** Factory for desired implementation of JavaScriptExecutor. */
   public ReactInstanceManagerBuilder setJavaScriptExecutorFactory(
       @Nullable JavaScriptExecutorFactory javaScriptExecutorFactory) {
@@ -353,6 +361,7 @@ public class ReactInstanceManagerBuilder {
         mDevBundleDownloadListener,
         mMinNumShakes,
         mMinTimeLeftInFrameForNonBatchedOperationMs,
+        mJSIModulesPackage,
         mUIManagerProvider,
         mCustomPackagerCommandHandlers,
         mTMMDelegateBuilder,
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
index 6551937..3f1fb6f 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
@@ -10,6 +10,7 @@ package com.facebook.react;
 import android.app.Application;
 import androidx.annotation.Nullable;
 import com.facebook.infer.annotation.Assertions;
+import com.facebook.react.bridge.JSIModulePackage;
 import com.facebook.react.bridge.JavaScriptExecutorFactory;
 import com.facebook.react.bridge.ReactMarker;
 import com.facebook.react.bridge.ReactMarkerConstants;
@@ -84,6 +85,7 @@ public abstract class ReactNativeHost {
             .setLazyViewManagersEnabled(getLazyViewManagersEnabled())
             .setRedBoxHandler(getRedBoxHandler())
             .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
+            .setJSIModulesPackage(getJSIModulePackage())
             .setUIManagerProvider(getUIManagerProvider())
             .setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
             .setReactPackageTurboModuleManagerDelegateBuilder(
@@ -125,6 +127,10 @@ public abstract class ReactNativeHost {
     return mApplication;
   }
 
+  protected @Nullable JSIModulePackage getJSIModulePackage() {
+    return null;
+  }
+
   protected @Nullable UIManagerProvider getUIManagerProvider() {
     return reactApplicationContext -> null;
   }
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java
index b2e2d7c..0ee0dd6 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java
@@ -120,7 +120,7 @@ public interface CatalystInstance
   RuntimeScheduler getRuntimeScheduler();
 
   @Deprecated
-  <T extends JSIModule> void addJSIModules(List<JSIModuleSpec<T>> jsiModules);
+  void addJSIModules(List<JSIModuleSpec> jsiModules);
 
   /**
    * Returns a hybrid object that contains a pointer to a JS CallInvoker, which is used to schedule
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
index f86dffb..67e5e35 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java
@@ -537,8 +537,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
   public native RuntimeScheduler getRuntimeScheduler();
 
   @Override
-  @Deprecated
-  public <T extends JSIModule> void addJSIModules(List<JSIModuleSpec<T>> jsiModules) {
+  public void addJSIModules(List<JSIModuleSpec> jsiModules) {
     mJSIModuleRegistry.registerModules(jsiModules);
   }
   
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java
index 90d5cde..e81b361 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSIModuleRegistry.java
@@ -26,8 +26,8 @@ class JSIModuleRegistry {
     return Assertions.assertNotNull(jsiModuleHolder.getJSIModule());
   }
 
-  public <T extends JSIModule> void registerModules(List<JSIModuleSpec<T>> jsiModules) {
-    for (JSIModuleSpec<T> spec : jsiModules) {
+  public void registerModules(List<JSIModuleSpec> jsiModules) {
+    for (JSIModuleSpec spec : jsiModules) {
       mModules.put(spec.getJSIModuleType(), new JSIModuleHolder(spec));
     }
   }
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt
deleted file mode 100644
index 03a18c6..0000000
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-package com.facebook.react.runtime
-
-import android.content.res.AssetManager
-import com.facebook.proguard.annotations.DoNotStrip
-import com.facebook.react.bridge.CatalystInstance
-import com.facebook.react.bridge.JSIModule
-import com.facebook.react.bridge.JSIModuleSpec
-import com.facebook.react.bridge.JSIModuleType
-import com.facebook.react.bridge.JavaScriptContextHolder
-import com.facebook.react.bridge.JavaScriptModule
-import com.facebook.react.bridge.NativeArray
-import com.facebook.react.bridge.NativeArrayInterface
-import com.facebook.react.bridge.NativeModule
-import com.facebook.react.bridge.NativeModuleRegistry
-import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener
-import com.facebook.react.bridge.RuntimeExecutor
-import com.facebook.react.bridge.RuntimeScheduler
-import com.facebook.react.bridge.UIManager
-import com.facebook.react.bridge.queue.ReactQueueConfiguration
-import com.facebook.react.common.annotations.DeprecatedInNewArchitecture
-import com.facebook.react.common.annotations.VisibleForTesting
-import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry
-import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
-import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder
-
-@DoNotStrip
-@DeprecatedInNewArchitecture
-public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : CatalystInstance {
-
-  override fun handleMemoryPressure(level: Int) {
-    throw UnsupportedOperationException("Unimplemented method 'handleMemoryPressure'")
-  }
-
-  override fun loadScriptFromAssets(
-      assetManager: AssetManager,
-      assetURL: String,
-      loadSynchronously: Boolean
-  ) {
-    throw UnsupportedOperationException("Unimplemented method 'loadScriptFromAssets'")
-  }
-
-  override fun loadScriptFromFile(fileName: String, sourceURL: String, loadSynchronously: Boolean) {
-    throw UnsupportedOperationException("Unimplemented method 'loadScriptFromFile'")
-  }
-
-  override fun loadSplitBundleFromFile(fileName: String, sourceURL: String) {
-    throw UnsupportedOperationException("Unimplemented method 'loadSplitBundleFromFile'")
-  }
-
-  override fun setSourceURLs(deviceURL: String, remoteURL: String) {
-    throw UnsupportedOperationException("Unimplemented method 'setSourceURLs'")
-  }
-
-  override fun runJSBundle() {
-    throw UnsupportedOperationException("Unimplemented method 'runJSBundle'")
-  }
-
-  override fun hasRunJSBundle(): Boolean {
-    throw UnsupportedOperationException("Unimplemented method 'hasRunJSBundle'")
-  }
-
-  override fun getSourceURL(): String? {
-    throw UnsupportedOperationException("Unimplemented method 'getSourceURL'")
-  }
-
-  @DoNotStrip
-  override fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) {
-    throw UnsupportedOperationException("Unimplemented method 'invokeCallback'")
-  }
-
-  override fun callFunction(module: String, method: String, arguments: NativeArray) {
-    throw UnsupportedOperationException("Unimplemented method 'callFunction'")
-  }
-
-  override fun destroy() {
-    throw UnsupportedOperationException("Unimplemented method 'destroy'")
-  }
-
-  override fun isDestroyed(): Boolean {
-    throw UnsupportedOperationException("Unimplemented method 'isDestroyed'")
-  }
-
-  @VisibleForTesting
-  override fun initialize() {
-    throw UnsupportedOperationException("Unimplemented method 'initialize'")
-  }
-
-  override fun getReactQueueConfiguration(): ReactQueueConfiguration =
-      reactHost.reactQueueConfiguration!!
-
-  override fun <T : JavaScriptModule> getJSModule(jsInterface: Class<T>): T =
-      reactHost.currentReactContext?.getJSModule(jsInterface)!!
-
-  override fun <T : NativeModule> hasNativeModule(nativeModuleInterface: Class<T>): Boolean =
-      reactHost.hasNativeModule(nativeModuleInterface)
-
-  override fun <T : NativeModule> getNativeModule(nativeModuleInterface: Class<T>): T? =
-      reactHost.getNativeModule(nativeModuleInterface)
-
-  override fun getNativeModule(moduleName: String): NativeModule? =
-      reactHost.getNativeModule(moduleName)
-
-  @Deprecated(
-      message =
-          "getJSIModule(JSIModuleType moduleType) is deprecated and will be deleted in the future. Please use ReactInstanceEventListener to subscribe for react instance events instead.")
-  override fun getJSIModule(moduleType: JSIModuleType): JSIModule {
-    throw UnsupportedOperationException("Unimplemented method 'getJSIModule'")
-  }
-
-  override fun getNativeModules(): Collection<NativeModule> = reactHost.getNativeModules()
-
-  override fun extendNativeModules(modules: NativeModuleRegistry) {
-    throw UnsupportedOperationException("Unimplemented method 'extendNativeModules'")
-  }
-
-  override fun addBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) {
-    throw UnsupportedOperationException("Unimplemented method 'addBridgeIdleDebugListener'")
-  }
-
-  override fun removeBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) {
-    throw UnsupportedOperationException("Unimplemented method 'removeBridgeIdleDebugListener'")
-  }
-
-  override fun registerSegment(segmentId: Int, path: String) {
-    throw UnsupportedOperationException("Unimplemented method 'registerSegment'")
-  }
-
-  @VisibleForTesting
-  override fun setGlobalVariable(propName: String, jsonValue: String) {
-    throw UnsupportedOperationException("Unimplemented method 'setGlobalVariable'")
-  }
-
-  @Deprecated(message = "This API is unsupported in the New Architecture.")
-  override fun getJavaScriptContextHolder(): JavaScriptContextHolder? {
-    return reactHost.getJavaScriptContextHolder()
-  }
-
-  override fun getRuntimeExecutor(): RuntimeExecutor? {
-    return reactHost.getRuntimeExecutor()
-  }
-
-  override fun getRuntimeScheduler(): RuntimeScheduler {
-    throw UnsupportedOperationException("Unimplemented method 'getRuntimeScheduler'")
-  }
-
-  @Deprecated(message = "This API is unsupported in the New Architecture.")
-  override fun <T : JSIModule> addJSIModules(jsiModules: List<JSIModuleSpec<T>>) {
-    throw UnsupportedOperationException("Unimplemented method 'addJSIModules'")
-  }
-
-  override fun getJSCallInvokerHolder(): CallInvokerHolder? {
-    return reactHost.getJSCallInvokerHolder()
-  }
-
-  override fun getNativeMethodCallInvokerHolder(): NativeMethodCallInvokerHolder {
-    throw UnsupportedOperationException("Unimplemented method 'getNativeMethodCallInvokerHolder'")
-  }
-
-  @Deprecated(
-      message =
-          "setTurboModuleManager(JSIModule getter) is deprecated and will be deleted in the future. Please use setTurboModuleRegistry(TurboModuleRegistry turboModuleRegistry) instead.",
-      replaceWith = ReplaceWith("setTurboModuleRegistry(turboModuleRegistry)"))
-  override fun setTurboModuleManager(getter: JSIModule) {
-    throw UnsupportedOperationException("Unimplemented method 'setTurboModuleManager'")
-  }
-
-  @DeprecatedInNewArchitecture(
-      message =
-          "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.")
-  override fun setTurboModuleRegistry(turboModuleRegistry: TurboModuleRegistry) {
-    throw UnsupportedOperationException("Unimplemented method 'setTurboModuleRegistry'")
-  }
-
-  @DeprecatedInNewArchitecture(
-      message =
-          "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.")
-  override fun setFabricUIManager(fabricUIManager: UIManager) {
-    throw UnsupportedOperationException("Unimplemented method 'setFabricUIManager'")
-  }
-
-  @DeprecatedInNewArchitecture(
-      message =
-          "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.")
-  override fun getFabricUIManager(): UIManager {
-    throw UnsupportedOperationException("Unimplemented method 'getFabricUIManager'")
-  }
-}
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java
index d1cdaa9..24cc18a 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java
@@ -81,14 +81,6 @@ class BridgelessReactContext extends ReactApplicationContext implements EventDis
     return mReactHost.getUIManager();
   }
 
-  @Override
-  public CatalystInstance getCatalystInstance() {
-    Log.w(
-        TAG,
-        "[WARNING] Bridgeless doesn't support CatalystInstance. Accessing an API that's not part of the new architecture is not encouraged usage.");
-    return new BridgelessCatalystInstance(mReactHost);
-  }
-
   @Override
   public boolean hasActiveReactInstance() {
     return mReactHost.isInstanceInitialized();

Really frustrating that the RN changelog mentions that this was unused in open source, idk how they missed this huge repo

@LukasMod
Copy link

@radex Could you update how you see the progress of this work?

We're trying to get the app ready for the new architecture as of version 0.76 and are looking for issues among the key libraries. Without watermelonDB it probably won't be possible, so any information about the progress/plans would be super valuable. Maybe someone advanced from the community would be able to help solve some problems.

@swapnil001
Copy link

Hi, Any update on solution for this?

@prathameshmm02
Copy link

WatermelonDB JSI module can be registered like this in React Native 0.74+:

MainApplication.kt

import others....
import com.facebook.react.ReactInstanceManager
import com.nozbe.watermelondb.jsi.JSIInstaller
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.UiThreadUtil

class MainApplication : Application(), ReactApplication {
....
    private var listenerAdded = false
    override fun onCreate() {
        super.onCreate()
        ....
        registerJSIModules()
    }

    private fun runOnJSQueueThread(action: () -> Unit) {
        reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
            action()
        } ?: UiThreadUtil.runOnUiThread {
            reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
                action()
            }
        }
    }

    @Suppress("DEPRECATION")
    private fun registerJSIModules() {
        val reactInstanceManager = reactNativeHost.reactInstanceManager

        if (!listenerAdded) {
            listenerAdded = true
            reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
                override fun onReactContextInitialized(context: ReactContext) {
                    runOnJSQueueThread {
                        registerWatermelonJSI(context)
                    }
                }
            })
        }
    }

    private fun registerWatermelonJSI(context: ReactContext) {
        val holder = context.javaScriptContextHolder?.get()
        if (holder != null) {
            JSIInstaller.install(context, holder)
        }
    }
}

A patch is also required to make JSIInstaller class and methods public.

diff --git a/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java b/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
index 055cede2f20cd6a75ffb79d156e35396c1438c91..fb7ca33847aa9ad1349b10b35d4e27575e843479 100755
--- a/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
+++ b/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
@@ -1,8 +1,8 @@
 package com.nozbe.watermelondb.jsi;
 
 import android.content.Context;
-class JSIInstaller {
-    static void install(Context context, long javaScriptContextHolder) {
+public class JSIInstaller {
+    public static void install(Context context, long javaScriptContextHolder) {
         JSIInstaller.context = context;
         new JSIInstaller().installBinding(javaScriptContextHolder);
 

Reference: https://github.com/mattermost/mattermost-mobile/blob/main/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.kt

Didn't get the time to test this, but probably should work?

@enahum
Copy link

enahum commented Sep 27, 2024

The above works, but I have not tested it with 0.75.x

@ishan-chhabra
Copy link

Hi @prathameshmm02 @enahum!

I did try the workaround above but it did not work for me.
I still get this error while syncing in turbo.

Error while syncing [Error: provideSyncJson unavailable. Use JSI mode to enable.]

MainApplication.kt

package something.something

import android.app.Application
import android.content.res.Configuration
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import java.util.Arrays
import com.facebook.react.bridge.JSIModulePackage
import com.facebook.react.bridge.JSIModule
import com.facebook.react.bridge.JSIModuleSpec
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.JavaScriptContextHolder
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
import com.nozbe.watermelondb.jsi.WatermelonDBJSIPackage
import com.facebook.react.ReactInstanceManager
import com.nozbe.watermelondb.jsi.JSIInstaller
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.UiThreadUtil

class MainApplication : Application(), ReactApplication {

  private var listenerAdded = false
  override val reactNativeHost: ReactNativeHost =
      ReactNativeHostWrapper(this, object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              // Packages that cannot be autolinked yet can be added manually here, for example:
              // add(MyReactNativePackage())
            }

        override fun getJSMainModuleName(): String = "index"

        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
      })

  override val reactHost: ReactHost
    get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)

    override fun onCreate() {
    super.onCreate()
    
    SoLoader.init(this, false)
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      load()
    }
    ApplicationLifecycleDispatcher.onApplicationCreate(this)
    registerJSIModules()
  }

    private fun runOnJSQueueThread(action: () -> Unit) {
        reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
            action()
        } ?: UiThreadUtil.runOnUiThread {
            reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
                action()
            }
        }
    }

  @Suppress("DEPRECATION")
  private fun registerJSIModules() {
    val reactInstanceManager = reactNativeHost.reactInstanceManager

    if (!listenerAdded) {
        listenerAdded = true
        reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
          override fun onReactContextInitialized(context: ReactContext) {
              runOnJSQueueThread {
                registerWatermelonJSI(context)
              }
            }
          })
        }
    }

  private fun registerWatermelonJSI(context: ReactContext) {
      val holder = context.javaScriptContextHolder?.get()
      if (holder != null) {
          JSIInstaller.install(context, holder)
      }
  }

  override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
  }
}

I also patched WatermelonDB as follows:

@nozbe+watermelondb+0.27.1.patch

diff --git a/node_modules/@nozbe/watermelondb/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java b/node_modules/@nozbe/watermelondb/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
index 055cede..fb7ca33 100755
--- a/node_modules/@nozbe/watermelondb/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
+++ b/node_modules/@nozbe/watermelondb/native/android-jsi/src/main/java/com/nozbe/watermelondb/jsi/JSIInstaller.java
@@ -1,8 +1,8 @@
 package com.nozbe.watermelondb.jsi;
 
 import android.content.Context;
-class JSIInstaller {
-    static void install(Context context, long javaScriptContextHolder) {
+public class JSIInstaller {
+    public static void install(Context context, long javaScriptContextHolder) {
         JSIInstaller.context = context;
         new JSIInstaller().installBinding(javaScriptContextHolder);

I have also verified that all the changes stated in JSI setup on https://watermelondb.dev/docs/Installation#android-react-native are applied.

Any help will be greatly appreciated. Thank youu! :)

@prathameshmm02
Copy link

prathameshmm02 commented Sep 29, 2024

@ishan-chhabra Do you have JSI enabled in your adapter?

const adapter = new SQLiteAdapter({
  schema,
  migrations,
  jsi: true,
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests