Skip to content

Conversation

@tjzel
Copy link
Collaborator

@tjzel tjzel commented Dec 1, 2025

Summary

Requires:

Currently we don't denote what headers are public and what are private in worklets. While the sole distinction is out of scope of this PR and something for the future, we could implement how the user is supposed to import them.

iOS (Cocoapods)

With Cocoapods the issue is quite straightforward. Running pod install will create a directory Pods/Headers with the following structure (only RNWorklets included), having Public and Private dirs:

.
├── Private
│   └── RNWorklets
│       └── worklets
│           ├── AnimationFrameQueue
│           │   └── AnimationFrameBatchinator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.h
│           ├── apple
│           │   ├── AnimationFrameQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AnimationFrameQueue.h
│           │   ├── AssertJavaScriptQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AssertJavaScriptQueue.h
│           │   ├── AssertTurboModuleManagerQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AssertTurboModuleManagerQueue.h
│           │   ├── IOSUIScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/IOSUIScheduler.h
│           │   ├── SlowAnimations.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/SlowAnimations.h
│           │   ├── WorkletsDisplayLink.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsDisplayLink.h
│           │   ├── WorkletsMessageThread.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsMessageThread.h
│           │   └── WorkletsModule.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsModule.h
│           ├── NativeModules
│           │   ├── JSIWorkletsModuleProxy.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.h
│           │   └── WorkletsModuleProxy.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h
│           ├── Registries
│           │   ├── EventHandlerRegistry.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Registries/EventHandlerRegistry.h
│           │   └── WorkletRuntimeRegistry.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Registries/WorkletRuntimeRegistry.h
│           ├── Resources
│           │   └── Unpackers.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Resources/Unpackers.h
│           ├── RunLoop
│           │   ├── AsyncQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/AsyncQueue.h
│           │   ├── AsyncQueueImpl.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/AsyncQueueImpl.h
│           │   └── EventLoop.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/EventLoop.h
│           ├── SharedItems
│           │   ├── MemoryManager.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/MemoryManager.h
│           │   ├── Serializable.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h
│           │   ├── Synchronizable.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/Synchronizable.h
│           │   └── SynchronizableAccess.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/SynchronizableAccess.h
│           ├── Tools
│           │   ├── Defs.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/Defs.h
│           │   ├── FeatureFlags.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/FeatureFlags.h
│           │   ├── JSISerializer.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSISerializer.h
│           │   ├── JSLogger.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSLogger.h
│           │   ├── JSScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSScheduler.h
│           │   ├── PlatformLogger.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/PlatformLogger.h
│           │   ├── SingleInstanceChecker.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/SingleInstanceChecker.h
│           │   ├── ThreadSafeQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/ThreadSafeQueue.h
│           │   ├── UIScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/UIScheduler.h
│           │   ├── VersionUtils.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/VersionUtils.h
│           │   ├── WorkletEventHandler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.h
│           │   ├── WorkletsJSIUtils.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsJSIUtils.h
│           │   ├── WorkletsSystraceSection.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsSystraceSection.h
│           │   └── WorkletsVersion.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsVersion.h
│           └── WorkletRuntime
│               ├── RNRuntimeWorkletDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h
│               ├── RuntimeBindings.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeBindings.h
│               ├── RuntimeData.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeData.h
│               ├── RuntimeHolder.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeHolder.h
│               ├── RuntimeKind.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeKind.h
│               ├── RuntimeManager.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h
│               ├── UIRuntimeDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/UIRuntimeDecorator.h
│               ├── WorkletHermesRuntime.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletHermesRuntime.h
│               ├── WorkletRuntime.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h
│               ├── WorkletRuntimeCollector.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h
│               └── WorkletRuntimeDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.h
└── Public
    └── RNWorklets
        └── worklets
            ├── AnimationFrameQueue
            │   └── AnimationFrameBatchinator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.h
            ├── apple
            │   ├── AnimationFrameQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AnimationFrameQueue.h
            │   ├── AssertJavaScriptQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AssertJavaScriptQueue.h
            │   ├── AssertTurboModuleManagerQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/AssertTurboModuleManagerQueue.h
            │   ├── IOSUIScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/IOSUIScheduler.h
            │   ├── SlowAnimations.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/SlowAnimations.h
            │   ├── WorkletsDisplayLink.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsDisplayLink.h
            │   ├── WorkletsMessageThread.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsMessageThread.h
            │   └── WorkletsModule.h -> ../../../../../../../../../node_modules/react-native-worklets/apple/worklets/apple/WorkletsModule.h
            ├── NativeModules
            │   ├── JSIWorkletsModuleProxy.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.h
            │   └── WorkletsModuleProxy.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/NativeModules/WorkletsModuleProxy.h
            ├── Registries
            │   ├── EventHandlerRegistry.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Registries/EventHandlerRegistry.h
            │   └── WorkletRuntimeRegistry.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Registries/WorkletRuntimeRegistry.h
            ├── Resources
            │   └── Unpackers.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Resources/Unpackers.h
            ├── RunLoop
            │   ├── AsyncQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/AsyncQueue.h
            │   ├── AsyncQueueImpl.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/AsyncQueueImpl.h
            │   └── EventLoop.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/RunLoop/EventLoop.h
            ├── SharedItems
            │   ├── MemoryManager.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/MemoryManager.h
            │   ├── Serializable.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h
            │   ├── Synchronizable.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/Synchronizable.h
            │   └── SynchronizableAccess.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/SharedItems/SynchronizableAccess.h
            ├── Tools
            │   ├── Defs.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/Defs.h
            │   ├── FeatureFlags.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/FeatureFlags.h
            │   ├── JSISerializer.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSISerializer.h
            │   ├── JSLogger.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSLogger.h
            │   ├── JSScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/JSScheduler.h
            │   ├── PlatformLogger.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/PlatformLogger.h
            │   ├── SingleInstanceChecker.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/SingleInstanceChecker.h
            │   ├── ThreadSafeQueue.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/ThreadSafeQueue.h
            │   ├── UIScheduler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/UIScheduler.h
            │   ├── VersionUtils.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/VersionUtils.h
            │   ├── WorkletEventHandler.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.h
            │   ├── WorkletsJSIUtils.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsJSIUtils.h
            │   ├── WorkletsSystraceSection.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsSystraceSection.h
            │   └── WorkletsVersion.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/Tools/WorkletsVersion.h
            └── WorkletRuntime
                ├── RNRuntimeWorkletDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RNRuntimeWorkletDecorator.h
                ├── RuntimeBindings.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeBindings.h
                ├── RuntimeData.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeData.h
                ├── RuntimeHolder.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeHolder.h
                ├── RuntimeKind.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeKind.h
                ├── RuntimeManager.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h
                ├── UIRuntimeDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/UIRuntimeDecorator.h
                ├── WorkletHermesRuntime.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletHermesRuntime.h
                ├── WorkletRuntime.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h
                ├── WorkletRuntimeCollector.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeCollector.h
                └── WorkletRuntimeDecorator.h -> ../../../../../../../../../node_modules/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.h

You can see that all of the headers are actual symlinks to the source files - this is crucial for #pragma once, I will explain it later on.

Reading CocoaPods documentation:

If no public headers are specified then all the headers in source_files are considered public.

This is why all the headers are in the Public dir. AFAIK the Private dir always has all the headers.

The Public dir is in the include list, therefore to import i.e. AsyncQueue.h the user has to do #include <RNWorklets/worklets/WorkletRuntime/WorkletRuntime.h>.

We can easily turn this import to <RNWorklets/AsyncQueue.h> by specifying flattened, public headers as done in this PR.

react-native-reanimated doesn't do <RNWorklets/...> for import because it adds '"$(PODS_ROOT)/Headers/Public/RNWorklets"', to HEADER_SEARCH_PATHS for compilation - but it would be better if the user didn't have to do it.

Android

If the user consumes Worklets as a prefab (which is the preferred way) he automatically has access to the includes in the prefabs, with the following structure:

.
└── worklets
    ├── android
    │   ├── AndroidUIScheduler.h
    │   ├── AnimationFrameCallback.h
    │   └── WorkletsModule.h
    ├── AnimationFrameQueue
    │   └── AnimationFrameBatchinator.h
    ├── NativeModules
    │   ├── JSIWorkletsModuleProxy.h
    │   └── WorkletsModuleProxy.h
    ├── Registries
    │   ├── EventHandlerRegistry.h
    │   └── WorkletRuntimeRegistry.h
    ├── Resources
    │   └── Unpackers.h
    ├── RunLoop
    │   ├── AsyncQueue.h
    │   ├── AsyncQueueImpl.h
    │   └── EventLoop.h
    ├── SharedItems
    │   ├── MemoryManager.h
    │   ├── Serializable.h
    │   ├── Synchronizable.h
    │   └── SynchronizableAccess.h
    ├── Tools
    │   ├── Defs.h
    │   ├── FeatureFlags.h
    │   ├── JSISerializer.h
    │   ├── JSLogger.h
    │   ├── JSScheduler.h
    │   ├── PlatformLogger.h
    │   ├── SingleInstanceChecker.h
    │   ├── ThreadSafeQueue.h
    │   ├── UIScheduler.h
    │   ├── VersionUtils.h
    │   ├── WorkletEventHandler.h
    │   ├── WorkletsJSIUtils.h
    │   ├── WorkletsSystraceSection.h
    │   └── WorkletsVersion.h
    └── WorkletRuntime
        ├── RNRuntimeWorkletDecorator.h
        ├── RuntimeBindings.h
        ├── RuntimeData.h
        ├── RuntimeHolder.h
        ├── RuntimeKind.h
        ├── RuntimeManager.h
        ├── UIRuntimeDecorator.h
        ├── WorkletHermesRuntime.h
        ├── WorkletRuntime.h
        ├── WorkletRuntimeCollector.h
        └── WorkletRuntimeDecorator.h

To import, i.e. AsyncQueue.h the user has to do #include <worklets/WorkletRuntime/WorkletRuntime.h> and this way can also access all other "private" headers.

This include can be changed to <RNWorklets/...> only if we modify this header directory structure.

One option here would be to copy the public headers to the root of the inclusion tree, so it would look like

.
├── RNWorklets
│   ├── AsyncQueue.h
│   ├── Serializable.h
│   ├── Synchronizable.h
│   └── WorkletRuntime.h
└── worklets
    ├── ...
	...

However, this will break compilation as #pragma once inclusion guards are based on the file path. Therefore RNWorklets/AsyncQueue.h and worklets/RunLoop/AsyncQueue.h would be treated as different headers and re-compiled, resulting in duplicated symbols. Cocoapods resolves this issue by using symlinks everywhere.

We have two options to solve this problem:

  1. Change the #pragma once guards to
    	#ifndef HEADER_GUARD
    	#define HEADER_GUARD
    	
    	// code
    
    	#endif // HEADER_GUARD
    which is path-agnostic.
  2. Use symlinks just like Cocapods - by symlinking public headers in the prefab headers to the private headers.

The latter is less invasive for the time being - that's why I decided to opt into it.

Test plan

CI

Windows build CI: https://github.com/software-mansion/react-native-reanimated/actions/runs/19826776715

@tjzel tjzel changed the base branch from main to @tjzel/reanimated/use-fixed-prefab December 2, 2025 09:48
@tjzel tjzel marked this pull request as ready for review December 2, 2025 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants