Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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_supports_stable_api()
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",
Expand Down
27 changes: 27 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,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
}
Comment on lines +117 to +122

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.

Why is this needed?

Other lib checks don't have this, maybe it would be possible to fold it into getExternalLibVersion if necessary?


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"]
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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' }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
@ReactMethod
override fun installUIRuntimeBindings(): Boolean {
if (!uiRuntimeDecorated) {
uiRuntimeDecorated = decorateUIRuntime()
uiRuntimeDecorated = installUIRuntimeBindingsNative()
}

return uiRuntimeDecorated
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ target_link_libraries(
ReactAndroid::jsi
fbjni::fbjni
)

if(${RNGH_USE_WORKLETS})
Comment thread
Copilot marked this conversation as resolved.
Outdated
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()
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/Compat/StableApi.h>
#endif

namespace gesturehandler {
using namespace facebook;
using namespace facebook::react;
Expand All @@ -20,7 +24,8 @@ void RNGestureHandlerModule::registerNatives() {
"getBindingsInstallerCxx",
RNGestureHandlerModule::getBindingsInstallerCxx),
makeNativeMethod(
"decorateUIRuntime", RNGestureHandlerModule::decorateUIRuntime),
"installUIRuntimeBindingsNative",
RNGestureHandlerModule::installUIRuntimeBindings),
makeNativeMethod(
"invalidateNative", RNGestureHandlerModule::invalidateNative)});
}
Expand Down Expand Up @@ -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

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?

std::shared_ptr<worklets::WorkletRuntime> 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
Comment on lines +60 to +75

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.

Why isn't this a part of RNGHRuntimeDecorator::tryFindUIRuntime?


if (uiRuntime == nullptr) {
uiRuntime = RNGHRuntimeDecorator::tryFindUIRuntime(*rnRuntime_);
}

if (uiRuntime == nullptr) {
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::installUIRuntimeBindings(
*uiRuntime, [&](int handlerTag, int state) {
this->setGestureState(handlerTag, state);
});

return true;
}

void RNGestureHandlerModule::invalidateNative() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class RNGestureHandlerModule : public jni::HybridClass<RNGestureHandlerModule> {
jni::local_ref<BindingsInstallerHolder::javaobject> getBindingsInstallerCxx();

void setGestureState(const int handlerTag, const int state);
bool decorateUIRuntime();
bool installUIRuntimeBindings();
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/Compat/StableApi.h>)
#include <worklets/Compat/StableApi.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 @@ -104,16 +111,40 @@ - (void)initialize
_operations = [NSMutableArray new];
}

- (bool)decorateUIRuntime
- (bool)tryInstallUIRuntimeBindings
{
__weak RNGestureHandlerModule *weakSelf = self;
jsi::Runtime *uiRuntime = nullptr;

#if RNGH_HAS_WORKLETS
std::shared_ptr<worklets::WorkletRuntime> 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
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) {
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
Expand Down Expand Up @@ -185,7 +216,7 @@ - (void)flushOperations
- (nonnull NSNumber *)installUIRuntimeBindings
{
if (!_uiRuntimeDecorated) {
_uiRuntimeDecorated = [self decorateUIRuntime];
_uiRuntimeDecorated = [self tryInstallUIRuntimeBindings];
}

return _uiRuntimeDecorated ? @1 : @0;
Expand Down
Original file line number Diff line number Diff line change
@@ -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')

Expand Down Expand Up @@ -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
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::installUIRuntimeBindings(
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,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<uintptr_t *>(&arrayBufferValue[0]);
return reinterpret_cast<jsi::Runtime *>(*uiRuntimeAddress);
}

} // namespace gesturehandler
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class RNGHRuntimeDecorator {
jsi::Runtime &rnRuntime,
int moduleId,
std::function<void(int, int)> &&setGestureState);
static bool installUIRuntimeBindings(
jsi::Runtime &rnRuntime,
int moduleId,
static void installUIRuntimeBindings(
jsi::Runtime &uiRuntime,
std::function<void(int, int)> &&setGestureState);
static jsi::Runtime *tryFindUIRuntime(jsi::Runtime &rnRuntime);
};

} // namespace gesturehandler
4 changes: 4 additions & 0 deletions packages/react-native-gesture-handler/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

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.

Maybe we should disable var rule for whole file?

var __RNGH_UI_WORKLET_RUNTIME_HOLDER: object | undefined;
}
Loading