Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
3 changes: 2 additions & 1 deletion apps/basic-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- RNWorklets
- Yoga
- RNReanimated (4.4.1):
- hermes-engine
Expand Down Expand Up @@ -2307,7 +2308,7 @@ SPEC CHECKSUMS:
ReactCodegen: 21807c5e7d6d0e334667f755e23063834d581e62
ReactCommon: d5c1bb4427bf51c443de5926aac332c89ddd9363
ReactNativeDependencies: fa0a54b3f5319ae0e3b9aff32bfee7a424b88e66
RNGestureHandler: f07cf8b0a45a4eee18163629f78413b0a534aece
RNGestureHandler: 2d8900683c602d861399d08bac02cb91b683d298
RNReanimated: d6e6865fa9d5ce60863a9e244de45de4f9890e46
RNWorklets: 04a35c45bd72d24914cbaf24bdfa4e30e1eab2df
Yoga: fe50ab299e578f397fef753cf309c6703a4db29b
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Pod::Spec.new do |s|

install_modules_dependencies(s);

if GestureHandlerUtils.react_native_worklets_podspec_exists()
s.dependency "RNWorklets"
end

if ENV['USE_FRAMEWORKS'] != nil
add_dependency(s, "React-FabricComponents", :additional_framework_paths => [
"react/renderer/textlayoutmanager/platform/ios",
Expand Down
12 changes: 12 additions & 0 deletions packages/react-native-gesture-handler/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def shouldUseCommonInterfaceFromRNSVG() {
major > 15
}

def shouldUseRuntimeFromWorklets() {
return rootProject.subprojects.find { it.name == 'react-native-worklets' } != null
}
Comment thread
coado marked this conversation as resolved.
Outdated

def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
Expand Down Expand Up @@ -152,6 +156,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())
Expand Down Expand Up @@ -226,6 +231,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' }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.swmansion.gesturehandler.react
import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
Expand Down Expand Up @@ -120,7 +121,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
@ReactMethod
override fun installUIRuntimeBindings(): Boolean {
if (!uiRuntimeDecorated) {
uiRuntimeDecorated = decorateUIRuntime()
uiRuntimeDecorated = decorateUIRuntimeWithWorklets(getWorkletsModule()) || decorateUIRuntime()
}

return uiRuntimeDecorated
Expand Down Expand Up @@ -160,8 +161,19 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
}
}

private fun getWorkletsModule(): NativeModule? = try {
@Suppress("UNCHECKED_CAST")
val workletsModuleClass = Class.forName("com.swmansion.worklets.WorkletsModule") as Class<NativeModule>
reactApplicationContext.getNativeModule(workletsModuleClass)
} catch (_: ClassNotFoundException) {
null
} catch (_: ClassCastException) {
null
}

private external fun initHybrid(): HybridData
private external fun getBindingsInstallerCxx(): BindingsInstallerHolder
private external fun decorateUIRuntimeWithWorklets(workletsModule: Any?): Boolean
private external fun decorateUIRuntime(): Boolean
private external fun invalidateNative(): Unit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,23 @@ endif()

find_package(ReactAndroid REQUIRED CONFIG)
find_package(fbjni REQUIRED CONFIG)
if(${RNGH_USE_WORKLETS})
find_package(react-native-worklets REQUIRED CONFIG)
target_compile_definitions(${PACKAGE_NAME} PRIVATE RNGH_USE_WORKLETS=1)
target_include_directories(
${PACKAGE_NAME}
PRIVATE
"${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor"
)
Comment thread
coado marked this conversation as resolved.
Outdated
endif()

target_link_libraries(
${PACKAGE_NAME}
ReactAndroid::reactnative
ReactAndroid::jsi
fbjni::fbjni
)

if(${RNGH_USE_WORKLETS})
Comment thread
Copilot marked this conversation as resolved.
Outdated
target_link_libraries(${PACKAGE_NAME} react-native-worklets::worklets)
endif()
Comment thread
coado marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

#include "RNGestureHandlerModule.h"

#ifdef RNGH_USE_WORKLETS
#include <worklets/android/WorkletsModule.h>
#endif

namespace gesturehandler {
using namespace facebook;
using namespace facebook::react;
Expand All @@ -21,6 +25,9 @@ void RNGestureHandlerModule::registerNatives() {
RNGestureHandlerModule::getBindingsInstallerCxx),
makeNativeMethod(
"decorateUIRuntime", RNGestureHandlerModule::decorateUIRuntime),
makeNativeMethod(
"decorateUIRuntimeWithWorklets",
RNGestureHandlerModule::decorateUIRuntimeWithWorklets),
makeNativeMethod(
"invalidateNative", RNGestureHandlerModule::invalidateNative)});
}
Expand Down Expand Up @@ -58,6 +65,39 @@ bool RNGestureHandlerModule::decorateUIRuntime() {
});
}

bool RNGestureHandlerModule::decorateUIRuntimeWithWorklets(
jni::alias_ref<jobject> workletsModule) {
#ifdef RNGH_USE_WORKLETS

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#ifdef checks only if the value is defined, not if it's true - I guess there's no way that this will be defined, but not 1, right?

if (!workletsModule) {
return false;
}

const auto jWorkletsModule =
jni::static_ref_cast<worklets::WorkletsModule::javaobject>(
workletsModule);
const auto workletsModuleProxy =
jWorkletsModule->cthis()->getWorkletsModuleProxy();
if (!workletsModuleProxy) {
return false;
}

const auto uiWorkletRuntime = workletsModuleProxy->getUIWorkletRuntime();
if (!uiWorkletRuntime) {
return false;
}
Comment on lines +77 to +83

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be the one example when nesting these if reads easier


RNGHRuntimeDecorator::decorateUIRuntime(
uiWorkletRuntime->getJSIRuntime(), [&](int handlerTag, int state) {
this->setGestureState(handlerTag, state);
});

return true;
#else
(void)workletsModule;
return false;
#endif
}

void RNGestureHandlerModule::invalidateNative() {
// This is called when the module is being destroyed, so we need to clear
// the reference to the java part to avoid memory leaks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class RNGestureHandlerModule : public jni::HybridClass<RNGestureHandlerModule> {
jni::local_ref<BindingsInstallerHolder::javaobject> getBindingsInstallerCxx();

void setGestureState(const int handlerTag, const int state);
bool decorateUIRuntimeWithWorklets(jni::alias_ref<jobject> workletsModule);
bool decorateUIRuntime();
void invalidateNative();
int getModuleId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

#import "RNGHRuntimeDecorator.h"

#if __has_include(<worklets/apple/WorkletsModule.h>)
Comment thread
coado marked this conversation as resolved.
Outdated
#import <worklets/apple/WorkletsModule.h>
#define RNGH_HAS_WORKLETS 1
#else
#define RNGH_HAS_WORKLETS 0
#endif

#import "RNGestureHandler.h"
#import "RNGestureHandlerDirection.h"
#import "RNGestureHandlerState.h"
Expand Down Expand Up @@ -108,6 +115,26 @@ - (bool)decorateUIRuntime
{
__weak RNGestureHandlerModule *weakSelf = self;

#if RNGH_HAS_WORKLETS
WorkletsModule *workletsModule = (WorkletsModule *)[self.moduleRegistry moduleForName:"WorkletsModule"];
if (workletsModule != nil) {
auto workletsModuleProxy = [workletsModule getWorkletsModuleProxy];
if (workletsModuleProxy != nullptr) {
auto uiWorkletRuntime = workletsModuleProxy->getUIWorkletRuntime();
if (uiWorkletRuntime != nullptr) {
RNGHRuntimeDecorator::decorateUIRuntime(
uiWorkletRuntime->getJSIRuntime(), [weakSelf](int handlerTag, int state) {
RNGestureHandlerModule *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf setGestureState:state forHandler:handlerTag];
}
});
return true;
}
}
}
#endif
Comment on lines +117 to +127

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this


return RNGHRuntimeDecorator::installUIRuntimeBindings(*_rnRuntime, _moduleId, [weakSelf](int handlerTag, int state) {
RNGestureHandlerModule *strongSelf = weakSelf;
if (strongSelf != nil) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,28 @@ 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_podspec_exists()
package_dir = react_native_worklets_package_dir()

if package_dir == nil
return false
end

return File.exist?(File.join(package_dir, 'RNWorklets.podspec'))
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,9 @@ void RNGHRuntimeDecorator::installRNRuntimeBindings(
rnRuntime, "_RNGH_MODULE_ID", std::move(moduleIdValue));
}

bool RNGHRuntimeDecorator::installUIRuntimeBindings(
jsi::Runtime &rnRuntime,
int moduleId,
void RNGHRuntimeDecorator::decorateUIRuntime(
Comment thread
coado marked this conversation as resolved.
Outdated
jsi::Runtime &uiRuntime,
std::function<void(int, int)> &&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<uintptr_t *>(&arrayBufferValue[0]);
jsi::Runtime &uiRuntime =
*reinterpret_cast<jsi::Runtime *>(*uiRuntimeAddress);

auto setGestureStateSync = jsi::Function::createFromHostFunction(
uiRuntime,
jsi::PropNameID::forAscii(uiRuntime, "_setGestureStateSync"),
Expand All @@ -128,6 +112,28 @@ bool RNGHRuntimeDecorator::installUIRuntimeBindings(

uiRuntime.global().setProperty(
uiRuntime, "_setGestureStateSync", std::move(setGestureStateSync));
}

bool RNGHRuntimeDecorator::installUIRuntimeBindings(
Comment thread
coado marked this conversation as resolved.
Outdated
jsi::Runtime &rnRuntime,
int moduleId,
std::function<void(int, int)> &&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<uintptr_t *>(&arrayBufferValue[0]);
jsi::Runtime &uiRuntime =
*reinterpret_cast<jsi::Runtime *>(*uiRuntimeAddress);

decorateUIRuntime(uiRuntime, std::move(setGestureState));

auto moduleIdValue = jsi::Value(moduleId);
rnRuntime.global().setProperty(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class RNGHRuntimeDecorator {
jsi::Runtime &rnRuntime,
int moduleId,
std::function<void(int, int)> &&setGestureState);
static void decorateUIRuntime(
jsi::Runtime &uiRuntime,
std::function<void(int, int)> &&setGestureState);
static bool installUIRuntimeBindings(
jsi::Runtime &rnRuntime,
int moduleId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export type ReanimatedHandler<THandlerData> = {
context: ReanimatedContext<THandlerData>;
};

type WorkletsModule = {
scheduleOnUI?: (worklet: () => void) => void;
};

export type NativeEventsManager = new (component: {
props: Record<string, unknown>;
_componentRef: React.Ref<unknown>;
Expand Down Expand Up @@ -82,12 +86,11 @@ 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');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Worklets = require('react-native-worklets') as WorkletsModule;

// Make sure worklets are initialized before attempting to install UI runtime bindings
Worklets?.scheduleOnUI(() => {
Worklets.scheduleOnUI?.(() => {
Comment thread
coado marked this conversation as resolved.
Outdated
'worklet';
});

Expand All @@ -102,6 +105,12 @@ try {
);
}
});
} catch (e) {
// When 'react-native-worklets' is not available we want to quietly continue
}

try {
Reanimated = require('react-native-reanimated');
} catch (e) {
Comment thread
coado marked this conversation as resolved.
Outdated
// When 'react-native-reanimated' is not available we want to quietly continue
// @ts-ignore TS demands the variable to be initialized
Expand Down
Loading