diff --git a/apps/basic-example/ios/Podfile.lock b/apps/basic-example/ios/Podfile.lock index 24285befb2..169655d708 100644 --- a/apps/basic-example/ios/Podfile.lock +++ b/apps/basic-example/ios/Podfile.lock @@ -1844,6 +1844,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies + - RNWorklets (>= 0.8.0) - Yoga - RNReanimated (4.4.1): - hermes-engine @@ -2307,7 +2308,7 @@ SPEC CHECKSUMS: ReactCodegen: 21807c5e7d6d0e334667f755e23063834d581e62 ReactCommon: d5c1bb4427bf51c443de5926aac332c89ddd9363 ReactNativeDependencies: fa0a54b3f5319ae0e3b9aff32bfee7a424b88e66 - RNGestureHandler: f07cf8b0a45a4eee18163629f78413b0a534aece + RNGestureHandler: a2f04fb0e854b5280517dbfd0b77170897a44644 RNReanimated: d6e6865fa9d5ce60863a9e244de45de4f9890e46 RNWorklets: 04a35c45bd72d24914cbaf24bdfa4e30e1eab2df Yoga: fe50ab299e578f397fef753cf309c6703a4db29b diff --git a/packages/react-native-gesture-handler/RNGestureHandler.podspec b/packages/react-native-gesture-handler/RNGestureHandler.podspec index 57b87c0be5..59e6a209db 100644 --- a/packages/react-native-gesture-handler/RNGestureHandler.podspec +++ b/packages/react-native-gesture-handler/RNGestureHandler.podspec @@ -6,6 +6,8 @@ is_gh_example_app = ENV["GH_EXAMPLE_APP_NAME"] != nil compilation_metadata_dir = "CompilationDatabase" compilation_metadata_generation_flag = is_gh_example_app ? '-gen-cdb-fragment-path ' + compilation_metadata_dir : '' version_flag = "-DREACT_NATIVE_MINOR_VERSION=#{GestureHandlerUtils.get_react_native_minor_version()}" +use_worklets = GestureHandlerUtils.react_native_worklets_supports_stable_api() +worklets_flag = use_worklets ? '-DRNGH_USE_WORKLETS=1' : '' Pod::Spec.new do |s| # NPM package specification @@ -22,11 +24,15 @@ Pod::Spec.new do |s| s.requires_arc = true s.platforms = { ios: '11.0', tvos: '11.0', osx: '10.15', visionos: '1.0' } s.xcconfig = { - "OTHER_CFLAGS" => "$(inherited) #{compilation_metadata_generation_flag} #{version_flag}" + "OTHER_CFLAGS" => "$(inherited) #{compilation_metadata_generation_flag} #{version_flag} #{worklets_flag}" } install_modules_dependencies(s); + if use_worklets + s.dependency "RNWorklets", ">= 0.8.0" + end + if ENV['USE_FRAMEWORKS'] != nil add_dependency(s, "React-FabricComponents", :additional_framework_paths => [ "react/renderer/textlayoutmanager/platform/ios", diff --git a/packages/react-native-gesture-handler/android/build.gradle b/packages/react-native-gesture-handler/android/build.gradle index 3f5f514cc1..b6d00cbfb7 100644 --- a/packages/react-native-gesture-handler/android/build.gradle +++ b/packages/react-native-gesture-handler/android/build.gradle @@ -107,6 +107,25 @@ def shouldUseCommonInterfaceFromRNSVG() { major > 15 } +def shouldUseRuntimeFromWorklets() { + def worklets = rootProject.subprojects.find { it.name == 'react-native-worklets' } + + if (worklets == null) { + return false + } + + def packageJson = new JsonSlurper().parseText(new File(worklets.projectDir, "../package.json").text) + def version = packageJson.version as String + + if (version.contains('-')) { + return false + } + + def (major, minor, patch) = getExternalLibVersion(worklets) + + return major > 0 || (major == 0 && minor >= 8) +} + def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] @@ -152,6 +171,7 @@ android { cppFlags "-O2", "-frtti", "-fexceptions", "-Wall", "-Werror", "-std=c++20", "-DANDROID" arguments "-DREACT_NATIVE_DIR=${REACT_NATIVE_DIR}", "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}", + "-DRNGH_USE_WORKLETS=${shouldUseRuntimeFromWorklets()}", "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" abiFilters(*reactNativeArchitectures()) @@ -226,6 +246,13 @@ dependencies { } } + if (shouldUseRuntimeFromWorklets()) { + implementation(rootProject.subprojects.find { it.name == 'react-native-worklets' }) { + // resolves "Duplicate class com.facebook.jni.CppException" + exclude group: 'com.facebook.fbjni' + } + } + if (shouldUseCommonInterfaceFromRNSVG()) { implementation rootProject.subprojects.find { it.name == 'react-native-svg' } } diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt index 046f404eba..3f3dde2b5d 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt @@ -120,7 +120,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : @ReactMethod override fun installUIRuntimeBindings(): Boolean { if (!uiRuntimeDecorated) { - uiRuntimeDecorated = decorateUIRuntime() + uiRuntimeDecorated = installUIRuntimeBindingsNative() } return uiRuntimeDecorated @@ -162,7 +162,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : private external fun initHybrid(): HybridData private external fun getBindingsInstallerCxx(): BindingsInstallerHolder - private external fun decorateUIRuntime(): Boolean + private external fun installUIRuntimeBindingsNative(): Boolean private external fun invalidateNative(): Unit override fun getBindingsInstaller() = getBindingsInstallerCxx() diff --git a/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt b/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt index 8c0dc69242..872b685a1e 100644 --- a/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt +++ b/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt @@ -51,3 +51,9 @@ target_link_libraries( ReactAndroid::jsi fbjni::fbjni ) + +if(RNGH_USE_WORKLETS) + find_package(react-native-worklets REQUIRED CONFIG) + target_compile_definitions(${PACKAGE_NAME} PRIVATE RNGH_USE_WORKLETS=1) + target_link_libraries(${PACKAGE_NAME} react-native-worklets::worklets) +endif() diff --git a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp index 476fe8dc3b..e21cc2bf65 100644 --- a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp +++ b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp @@ -3,6 +3,10 @@ #include "RNGestureHandlerModule.h" +#ifdef RNGH_USE_WORKLETS +#include +#endif + namespace gesturehandler { using namespace facebook; using namespace facebook::react; @@ -20,7 +24,8 @@ void RNGestureHandlerModule::registerNatives() { "getBindingsInstallerCxx", RNGestureHandlerModule::getBindingsInstallerCxx), makeNativeMethod( - "decorateUIRuntime", RNGestureHandlerModule::decorateUIRuntime), + "installUIRuntimeBindingsNative", + RNGestureHandlerModule::installUIRuntimeBindings), makeNativeMethod( "invalidateNative", RNGestureHandlerModule::invalidateNative)}); } @@ -51,11 +56,38 @@ void RNGestureHandlerModule::setGestureState( method(this->javaPart_, handlerTag, state); } -bool RNGestureHandlerModule::decorateUIRuntime() { - return RNGHRuntimeDecorator::installUIRuntimeBindings( - *rnRuntime_, getModuleId(), [&](int handlerTag, int state) { +bool RNGestureHandlerModule::installUIRuntimeBindings() { + jsi::Runtime *uiRuntime = nullptr; + +#ifdef RNGH_USE_WORKLETS + std::shared_ptr uiWorkletRuntime; + const auto runtimeHolder = rnRuntime_->global().getProperty( + *rnRuntime_, "__RNGH_UI_WORKLET_RUNTIME_HOLDER"); + + if (runtimeHolder.isObject()) { + uiWorkletRuntime = worklets::getWorkletRuntimeFromHolder( + *rnRuntime_, runtimeHolder.asObject(*rnRuntime_)); + + if (uiWorkletRuntime) { + uiRuntime = &worklets::getJSIRuntimeFromWorkletRuntime(uiWorkletRuntime); + } + } +#endif + + if (uiRuntime == nullptr) { + uiRuntime = RNGHRuntimeDecorator::tryFindUIRuntime(*rnRuntime_); + } + + if (uiRuntime == nullptr) { + return false; + } + + RNGHRuntimeDecorator::installUIRuntimeBindings( + *uiRuntime, [&](int handlerTag, int state) { this->setGestureState(handlerTag, state); }); + + return true; } void RNGestureHandlerModule::invalidateNative() { diff --git a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.h b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.h index d8e96afa2e..52d2dd2a89 100644 --- a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.h +++ b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.h @@ -27,7 +27,7 @@ class RNGestureHandlerModule : public jni::HybridClass { jni::local_ref getBindingsInstallerCxx(); void setGestureState(const int handlerTag, const int state); - bool decorateUIRuntime(); + bool installUIRuntimeBindings(); void invalidateNative(); int getModuleId(); }; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm index d848f228f0..a2ac8bda4e 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm @@ -16,6 +16,10 @@ #import "RNGHRuntimeDecorator.h" +#ifdef RNGH_USE_WORKLETS +#include +#endif + #import "RNGestureHandler.h" #import "RNGestureHandlerDirection.h" #import "RNGestureHandlerState.h" @@ -104,16 +108,40 @@ - (void)initialize _operations = [NSMutableArray new]; } -- (bool)decorateUIRuntime +- (bool)tryInstallUIRuntimeBindings { __weak RNGestureHandlerModule *weakSelf = self; + jsi::Runtime *uiRuntime = nullptr; + +#ifdef RNGH_USE_WORKLETS + std::shared_ptr uiWorkletRuntime; + const auto runtimeHolder = _rnRuntime->global().getProperty(*_rnRuntime, "__RNGH_UI_WORKLET_RUNTIME_HOLDER"); + + if (runtimeHolder.isObject()) { + uiWorkletRuntime = worklets::getWorkletRuntimeFromHolder(*_rnRuntime, runtimeHolder.asObject(*_rnRuntime)); + + if (uiWorkletRuntime != nullptr) { + uiRuntime = &worklets::getJSIRuntimeFromWorkletRuntime(uiWorkletRuntime); + } + } +#endif - return RNGHRuntimeDecorator::installUIRuntimeBindings(*_rnRuntime, _moduleId, [weakSelf](int handlerTag, int state) { + if (uiRuntime == nullptr) { + uiRuntime = RNGHRuntimeDecorator::tryFindUIRuntime(*_rnRuntime); + } + + if (uiRuntime == nullptr) { + return false; + } + + RNGHRuntimeDecorator::installUIRuntimeBindings(*uiRuntime, [weakSelf](int handlerTag, int state) { RNGestureHandlerModule *strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf setGestureState:state forHandler:handlerTag]; } }); + + return true; } - (void)createGestureHandler:(NSString *)handlerName handlerTag:(double)handlerTag config:(NSDictionary *)config @@ -185,7 +213,7 @@ - (void)flushOperations - (nonnull NSNumber *)installUIRuntimeBindings { if (!_uiRuntimeDecorated) { - _uiRuntimeDecorated = [self decorateUIRuntime]; + _uiRuntimeDecorated = [self tryInstallUIRuntimeBindings]; } return _uiRuntimeDecorated ? @1 : @0; diff --git a/packages/react-native-gesture-handler/scripts/gesture_handler_utils.rb b/packages/react-native-gesture-handler/scripts/gesture_handler_utils.rb index cfecb9f4dd..44ed4e4d5d 100644 --- a/packages/react-native-gesture-handler/scripts/gesture_handler_utils.rb +++ b/packages/react-native-gesture-handler/scripts/gesture_handler_utils.rb @@ -1,6 +1,10 @@ +require 'rubygems' + module GestureHandlerUtils module_function + MIN_REACT_NATIVE_WORKLETS_VERSION = Gem::Version.new('0.8.0') + def try_to_parse_react_native_package_json(react_native_dir) react_native_package_json_path = File.join(react_native_dir, 'package.json') @@ -28,4 +32,37 @@ def get_react_native_minor_version() return react_native_json['version'].split('.')[1].to_i end + + def node_package_dir(package_name) + package_json_path = `cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('#{package_name}/package.json')" 2>/dev/null`.strip + + if !$?.success? || package_json_path.empty? + return nil + end + + return File.dirname(package_json_path) + end + + def react_native_worklets_package_dir() + return node_package_dir('react-native-worklets') + end + + def react_native_worklets_supports_stable_api() + package_dir = react_native_worklets_package_dir() + + if package_dir == nil || !File.exist?(File.join(package_dir, 'RNWorklets.podspec')) + return false + end + + package_json = JSON.parse(File.read(File.join(package_dir, 'package.json'))) + version_string = package_json['version'] + + return false if version_string.include?('-') + + version = Gem::Version.new(version_string) + + return version >= MIN_REACT_NATIVE_WORKLETS_VERSION + rescue JSON::ParserError, ArgumentError, TypeError, Errno::ENOENT + return false + end end diff --git a/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.cpp b/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.cpp index 4f08af9b23..51431a783a 100644 --- a/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.cpp +++ b/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.cpp @@ -89,25 +89,9 @@ void RNGHRuntimeDecorator::installRNRuntimeBindings( rnRuntime, "_RNGH_MODULE_ID", std::move(moduleIdValue)); } -bool RNGHRuntimeDecorator::installUIRuntimeBindings( - jsi::Runtime &rnRuntime, - int moduleId, +void RNGHRuntimeDecorator::installUIRuntimeBindings( + jsi::Runtime &uiRuntime, std::function &&setGestureState) { - const auto runtimeHolder = - rnRuntime.global().getProperty(rnRuntime, "_WORKLET_RUNTIME"); - - if (runtimeHolder.isUndefined()) { - return false; - } - - const auto arrayBufferValue = - runtimeHolder.getObject(rnRuntime).getArrayBuffer(rnRuntime).data( - rnRuntime); - const auto uiRuntimeAddress = - reinterpret_cast(&arrayBufferValue[0]); - jsi::Runtime &uiRuntime = - *reinterpret_cast(*uiRuntimeAddress); - auto setGestureStateSync = jsi::Function::createFromHostFunction( uiRuntime, jsi::PropNameID::forAscii(uiRuntime, "_setGestureStateSync"), @@ -128,12 +112,22 @@ bool RNGHRuntimeDecorator::installUIRuntimeBindings( uiRuntime.global().setProperty( uiRuntime, "_setGestureStateSync", std::move(setGestureStateSync)); +} - auto moduleIdValue = jsi::Value(moduleId); - rnRuntime.global().setProperty( - rnRuntime, "_RNGH_MODULE_ID", std::move(moduleIdValue)); +jsi::Runtime *RNGHRuntimeDecorator::tryFindUIRuntime(jsi::Runtime &rnRuntime) { + const auto runtimeHolder = + rnRuntime.global().getProperty(rnRuntime, "_WORKLET_RUNTIME"); + + if (runtimeHolder.isUndefined()) { + return nullptr; + } - return true; + const auto arrayBufferValue = + runtimeHolder.getObject(rnRuntime).getArrayBuffer(rnRuntime).data( + rnRuntime); + const auto uiRuntimeAddress = + reinterpret_cast(&arrayBufferValue[0]); + return reinterpret_cast(*uiRuntimeAddress); } } // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.h b/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.h index 146c403858..0106b2f592 100644 --- a/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.h +++ b/packages/react-native-gesture-handler/shared/runtime/RNGHRuntimeDecorator.h @@ -10,10 +10,10 @@ class RNGHRuntimeDecorator { jsi::Runtime &rnRuntime, int moduleId, std::function &&setGestureState); - static bool installUIRuntimeBindings( - jsi::Runtime &rnRuntime, - int moduleId, + static void installUIRuntimeBindings( + jsi::Runtime &uiRuntime, std::function &&setGestureState); + static jsi::Runtime *tryFindUIRuntime(jsi::Runtime &rnRuntime); }; } // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/src/__tests__/RuntimeBindings.test.ts b/packages/react-native-gesture-handler/src/__tests__/RuntimeBindings.test.ts new file mode 100644 index 0000000000..0b8e98d593 --- /dev/null +++ b/packages/react-native-gesture-handler/src/__tests__/RuntimeBindings.test.ts @@ -0,0 +1,84 @@ +type RuntimeProviders = { + worklets?: { + getUIRuntimeHolder?: () => object; + }; + reanimated?: { + useSharedValue: unknown; + }; +}; + +function loadReanimatedWrapper( + providers: RuntimeProviders, + installUIRuntimeBindings: jest.Mock +) { + jest.doMock('../ghQueueMicrotask', () => ({ + ghQueueMicrotask: (callback: () => void) => callback(), + })); + jest.doMock('../v3/NativeProxy', () => ({ + NativeProxy: { installUIRuntimeBindings }, + })); + jest.doMock('react-native-worklets', () => { + if (providers.worklets === undefined) { + throw new Error('react-native-worklets is unavailable'); + } + + return providers.worklets; + }); + jest.doMock('react-native-reanimated', () => { + if (providers.reanimated === undefined) { + throw new Error('react-native-reanimated is unavailable'); + } + + return providers.reanimated; + }); + + jest.isolateModules(() => { + require('../handlers/gestures/reanimatedWrapper'); + }); +} + +beforeEach(() => { + jest.resetModules(); + globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER = undefined; +}); + +test('passes the Worklets runtime holder to native during installation', () => { + const runtimeHolder = {}; + let holderDuringInstallation: object | undefined; + const installUIRuntimeBindings = jest.fn(() => { + holderDuringInstallation = globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER; + return true; + }); + + loadReanimatedWrapper( + { + worklets: { getUIRuntimeHolder: () => runtimeHolder }, + reanimated: { useSharedValue: true }, + }, + installUIRuntimeBindings + ); + + expect(installUIRuntimeBindings).toHaveBeenCalledTimes(1); + expect(holderDuringInstallation).toBe(runtimeHolder); + expect(globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER).toBeUndefined(); +}); + +test('installs legacy bindings when Reanimated is available without Worklets', () => { + const installUIRuntimeBindings = jest.fn(() => true); + + loadReanimatedWrapper( + { reanimated: { useSharedValue: true } }, + installUIRuntimeBindings + ); + + expect(installUIRuntimeBindings).toHaveBeenCalledTimes(1); + expect(globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER).toBeUndefined(); +}); + +test('does not install bindings when no runtime provider is available', () => { + const installUIRuntimeBindings = jest.fn(() => true); + + loadReanimatedWrapper({}, installUIRuntimeBindings); + + expect(installUIRuntimeBindings).not.toHaveBeenCalled(); +}); diff --git a/packages/react-native-gesture-handler/src/global.d.ts b/packages/react-native-gesture-handler/src/global.d.ts index b20e3072ae..7d5ce1dcf9 100644 --- a/packages/react-native-gesture-handler/src/global.d.ts +++ b/packages/react-native-gesture-handler/src/global.d.ts @@ -10,4 +10,8 @@ declare global { var _setGestureStateAsync: | ((handlerTag: number, state: number) => void) | undefined; + + // Temporary holder used to pass the Worklets UI runtime to native code. + // eslint-disable-next-line no-var + var __RNGH_UI_WORKLET_RUNTIME_HOLDER: object | undefined; } diff --git a/packages/react-native-gesture-handler/src/handlers/gestures/reanimatedWrapper.ts b/packages/react-native-gesture-handler/src/handlers/gestures/reanimatedWrapper.ts index a107e9ea14..8a91e7d5c6 100644 --- a/packages/react-native-gesture-handler/src/handlers/gestures/reanimatedWrapper.ts +++ b/packages/react-native-gesture-handler/src/handlers/gestures/reanimatedWrapper.ts @@ -32,6 +32,10 @@ export type ReanimatedHandler = { context: ReanimatedContext; }; +type WorkletsModule = { + getUIRuntimeHolder?: () => object; +}; + export type NativeEventsManager = new (component: { props: Record; _componentRef: React.Ref; @@ -81,27 +85,18 @@ let Reanimated: } | undefined; -try { - Reanimated = require('react-native-reanimated'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires - const Worklets = require('react-native-worklets'); +let uiRuntimeHolder: object | undefined; - // Make sure worklets are initialized before attempting to install UI runtime bindings - Worklets?.scheduleOnUI(() => { - 'worklet'; - }); +try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Worklets = require('react-native-worklets') as WorkletsModule; + uiRuntimeHolder = Worklets?.getUIRuntimeHolder?.(); +} catch (e) { + // When 'react-native-worklets' is not available we want to quietly continue +} - ghQueueMicrotask(() => { - const decorated = NativeProxy.installUIRuntimeBindings(); - - if (!decorated) { - console.warn( - tagMessage( - 'Failed to install UI runtime bindings. Please report this at https://github.com/software-mansion/react-native-gesture-handler/issues.' - ) - ); - } - }); +try { + Reanimated = require('react-native-reanimated'); } catch (e) { // When 'react-native-reanimated' is not available we want to quietly continue // @ts-ignore TS demands the variable to be initialized @@ -114,6 +109,27 @@ if (!Reanimated?.useSharedValue) { Reanimated = undefined; } +if (uiRuntimeHolder !== undefined || Reanimated !== undefined) { + ghQueueMicrotask(() => { + globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER = uiRuntimeHolder; + + try { + const decorated = NativeProxy.installUIRuntimeBindings(); + + if (!decorated) { + console.warn( + tagMessage( + 'Failed to install UI runtime bindings. Please report this at https://github.com/software-mansion/react-native-gesture-handler/issues.' + ) + ); + } + } finally { + globalThis.__RNGH_UI_WORKLET_RUNTIME_HOLDER = undefined; + uiRuntimeHolder = undefined; + } + }); +} + if (Reanimated !== undefined && !Reanimated.setGestureState) { // The loaded module is Reanimated but it doesn't have the setGestureState defined Reanimated.setGestureState = () => {