Skip to content

Update how Gesture Handler exposes setGestureState to the Reanimated UI runtime #3207

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

Merged
merged 37 commits into from
Jul 7, 2025

Conversation

j-piasecki
Copy link
Member

@j-piasecki j-piasecki commented Nov 12, 2024

Description

Changes how setGestureState is exposed to the UI runtime. Instead of the weird conditionally adding Reanimated as a dependency on Android and the weird cast on iOS it uses _WORKLET_RUNTIME const injected by Reanimated into the JS runtime. This allows Gesture Handler to decorate the UI runtime without direct dependencies between the libraries.

The new approach relies on two methods being added to the global object:

  • _setGestureStateAsync on the JS runtime
  • _setGestureStateSync on the UI runtime

which allows for state manipulation also from the JS thread.

The basic example has been modified to easily test the new functionality.

Caution

This works only on the New Architecture (and breaks the old one)

Test plan

Test the expo example app and the modified basic example app

@j-piasecki j-piasecki requested a review from tomekzaw November 12, 2024 16:24
@j-piasecki j-piasecki force-pushed the @jpiasecki/update-reanimated-integration branch from 4b6cb29 to 039e228 Compare April 17, 2025 13:19
@tomekzaw tomekzaw changed the title Update how gesture handler exposes setGestureState to the ui runtime Update how Gesture Handler exposes setGestureState to the Reanimated UI runtime Apr 17, 2025
@tomekzaw
Copy link
Member

Additionally, I'd like to rename NativeMethods.h to GestureHandler.h or SetGestureState.h in react-native-reanimated, is that okay?

https://github.com/software-mansion/react-native-reanimated/blob/main/packages/react-native-reanimated/apple/reanimated/apple/native/NativeMethods.h

Copy link
Contributor

@tjzel tjzel left a comment

Choose a reason for hiding this comment

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

Very nice work overall, I like the removals 😎

@j-piasecki j-piasecki force-pushed the @jpiasecki/update-reanimated-integration branch from 08271fd to 12c9f11 Compare May 27, 2025 12:21
@j-piasecki j-piasecki force-pushed the @jpiasecki/update-reanimated-integration branch from 296b706 to d5d7233 Compare June 30, 2025 05:43
@j-piasecki j-piasecki force-pushed the @jpiasecki/update-reanimated-integration branch from 2e996c8 to eeafcc9 Compare July 2, 2025 11:55
@j-piasecki j-piasecki force-pushed the @jpiasecki/update-reanimated-integration branch from eeafcc9 to 801acd8 Compare July 3, 2025 05:25
@j-piasecki j-piasecki changed the base branch from main to next July 3, 2025 05:26
@j-piasecki j-piasecki requested review from m-bert and latekvo July 3, 2025 05:26
@j-piasecki j-piasecki marked this pull request as ready for review July 3, 2025 05:26
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this? We have format-apple script, the only difference is path passed to glob. Maybe it would be better to rename this script and pass path as argument?

Copy link
Member Author

Choose a reason for hiding this comment

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

Unified the scripts in da123f7. Let me know what you think.

@@ -49,10 +58,16 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
}

@ReactMethod
override fun createGestureHandler(handlerName: String, handlerTagDouble: Double, config: ReadableMap) {
override fun createGestureHandler(handlerName: String, handlerTagDouble: Double, config: ReadableMap): Boolean {
Copy link
Member

@latekvo latekvo Jul 3, 2025

Choose a reason for hiding this comment

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

The createGestureHandler seems to only ever return true/1.
Is this the case, or am I missing something?
If it is the case, can we get away with returning void instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

When a method returns any value in the codegen spec, the generated method is synchronous. I don't think there's a way to force codegen to generate a synchronous method that returns void.

Copy link
Contributor

@m-bert m-bert left a comment

Choose a reason for hiding this comment

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

LGTM (let's see what @tomekzaw thinks) 🎉

Copy link
Member

@tomekzaw tomekzaw left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for all the changes!

Copy link
Member

@WoLewicki WoLewicki left a comment

Choose a reason for hiding this comment

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

Some comments

jsi::Runtime &rnRuntime,
std::function<void(int, int)> &&setGestureState) {
const auto runtimeHolder =
rnRuntime.global().getProperty(rnRuntime, "_WORKLET_RUNTIME");
Copy link
Member

Choose a reason for hiding this comment

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

Do I get it correctly that this is added to rnRuntime by Reanimated? From which version of lib is it there? Also, can't we have some race conditions that it is not yet there when you call this method or something like that? Btw, is it safe to have some kind of pointer from one runtime to another?

Copy link
Member Author

Choose a reason for hiding this comment

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

Do I get it correctly that this is added to rnRuntime by Reanimated

Yes

From which version of lib is it there

Pretty sure it was added in 2.x

Also, can't we have some race conditions that it is not yet there when you call this method or something like that

Yes - when you don't render an Animated.View, the Reanimated native module will not be initialized, and this field will not exist. This is handled on the JS side which will throw an exception about this.

is it safe to have some kind of pointer from one runtime to another

I believe so

Copy link
Member

@tomekzaw tomekzaw Jul 4, 2025

Choose a reason for hiding this comment

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

Just for the context, _WORKLET_RUNTIME was added a few years ago to support integration of Reanimated with Expo GL. The latter could be used from the Reanimated UI runtime so it would inject some JSI bindings into the Reanimated UI runtime.

rnRuntime);
const auto uiRuntimeAddress =
reinterpret_cast<uintptr_t *>(&arrayBufferValue[0]);
jsi::Runtime &uiRuntime =
Copy link
Member

Choose a reason for hiding this comment

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

Oh ok, so the above is just a bool specifying that there will be a buffer with a pointer to memory with uiRuntime allocated there?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure what you mean by "the above".

runtimeHolder is a jsi value holding an array buffer. arrayBufferValue is the data held inside the buffer (the 8 bytes of the pointer to the ui runtime). uiRuntimeAddress is a pointer to the first byte of the buffer value, casted to uintptr_t which is an integer the size of a pointer. uiRuntime is a reference to the ui runtime which we get by casting the uiRuntimeAddress to jsi::Runtime * and dereferencing it.

Copy link
Member

Choose a reason for hiding this comment

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

Ok got it, thanks

Copy link
Member

Choose a reason for hiding this comment

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

For the context, this is because we can't represent a 64-bit pointer as a number so we need to use ArrayBuffer for that.

@j-piasecki j-piasecki merged commit 8a8b506 into next Jul 7, 2025
10 checks passed
@j-piasecki j-piasecki deleted the @jpiasecki/update-reanimated-integration branch July 7, 2025 07:40
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

Successfully merging this pull request may close these issues.

6 participants