From b539a7e12cd477833f6c5bcc761060851642b5b9 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Fri, 16 May 2025 07:52:58 -0700 Subject: [PATCH 1/4] remove return value from synchronouslyUpdateViewOnUIThread Differential Revision: D74884875 --- packages/react-native/React/Fabric/RCTSurfacePresenter.h | 2 +- .../react-native/React/Fabric/RCTSurfacePresenter.mm | 9 ++++----- .../react-native/React/Modules/RCTSurfacePresenterStub.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.h b/packages/react-native/React/Fabric/RCTSurfacePresenter.h index 7c28b3773624ca..f6bc2cc4d0a10f 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.h +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.h @@ -65,7 +65,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag; -- (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props; +- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props; - (void)setupAnimationDriverWithSurfaceHandler:(const facebook::react::SurfaceHandler &)surfaceHandler; diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm index ad1abd5ec2498c..48f9410a8cab46 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -148,28 +148,27 @@ - (UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag return componentView; } -- (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props +- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props { RCTScheduler *scheduler = [self scheduler]; if (!scheduler) { - return NO; + return; } ReactTag tag = [reactTag integerValue]; UIView *componentView = [_mountingManager.componentViewRegistry findComponentViewWithTag:tag]; if (componentView == nil) { - return NO; // This view probably isn't managed by Fabric + return; // This view probably isn't managed by Fabric } ComponentHandle handle = [[componentView class] componentDescriptorProvider].handle; auto *componentDescriptor = [scheduler findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN:handle]; if (!componentDescriptor) { - return YES; + return; } [_mountingManager synchronouslyUpdateViewOnUIThread:tag changedProps:props componentDescriptor:*componentDescriptor]; - return YES; } - (void)setupAnimationDriverWithSurfaceHandler:(const facebook::react::SurfaceHandler &)surfaceHandler diff --git a/packages/react-native/React/Modules/RCTSurfacePresenterStub.h b/packages/react-native/React/Modules/RCTSurfacePresenterStub.h index 9cf334854744ca..aea607083a0594 100644 --- a/packages/react-native/React/Modules/RCTSurfacePresenterStub.h +++ b/packages/react-native/React/Modules/RCTSurfacePresenterStub.h @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)createFabricSurfaceForModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties; - (nullable UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag; -- (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props; +- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props; - (void)addObserver:(id)observer; - (void)removeObserver:(id)observer; From 795b9a1004038b494b552d97a1d8b11b16e68b38 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Fri, 16 May 2025 08:10:26 -0700 Subject: [PATCH 2/4] introduce an option to drive animations with folly::dynamic Differential Revision: D74885607 --- .../Fabric/Mounting/RCTMountingManager.h | 2 +- .../Fabric/Mounting/RCTMountingManager.mm | 19 ++++++++++++++----- .../React/Fabric/RCTSurfacePresenter.h | 1 + .../React/Fabric/RCTSurfacePresenter.mm | 11 +++++++++-- .../platform/ios/react/utils/FollyConvert.h | 3 ++- .../platform/ios/react/utils/FollyConvert.mm | 16 ++++++++++++++++ 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.h b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.h index d9b2c79bb3f87e..ceac3eecb0978e 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.h +++ b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.h @@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN forShadowView:(const facebook::react::ShadowView &)shadowView; - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag - changedProps:(NSDictionary *)props + changedProps:(folly::dynamic)props componentDescriptor:(const facebook::react::ComponentDescriptor &)componentDescriptor; @end diff --git a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm index 2cd125a5cc145c..e6503ffd622467 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm @@ -282,25 +282,34 @@ - (void)setIsJSResponder:(BOOL)isJSResponder } - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag - changedProps:(NSDictionary *)props + changedProps:(folly::dynamic)props componentDescriptor:(const ComponentDescriptor &)componentDescriptor { RCTAssertMainQueue(); + NSArray *propsKeysToBeUpdated = extractKeysFromFollyDynamic(props); + bool updatesTransform = props.find("transform") != props.items().end(); + bool updatesOpacity = props.find("opacity") != props.items().end(); + UIView *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag]; + if (!componentView) { + RCTLogWarn(@"Attempted to update view with tag %ld, but it no longer exists", (long)reactTag); + return; + } + SurfaceId surfaceId = RCTSurfaceIdForView(componentView); Props::Shared oldProps = [componentView props]; Props::Shared newProps = componentDescriptor.cloneProps( - PropsParserContext{surfaceId, *_contextContainer.get()}, oldProps, RawProps(convertIdToFollyDynamic(props))); + PropsParserContext{surfaceId, *_contextContainer.get()}, oldProps, RawProps(std::move(props))); NSSet *propKeys = componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN ?: [NSSet new]; - propKeys = [propKeys setByAddingObjectsFromArray:props.allKeys]; + propKeys = [propKeys setByAddingObjectsFromArray:propsKeysToBeUpdated]; componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil; [componentView updateProps:newProps oldProps:oldProps]; componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = propKeys; const auto &newViewProps = static_cast(*newProps); - if (props[@"transform"]) { + if (updatesTransform) { auto layoutMetrics = LayoutMetrics(); layoutMetrics.frame.size.width = componentView.layer.bounds.size.width; layoutMetrics.frame.size.height = componentView.layer.bounds.size.height; @@ -309,7 +318,7 @@ - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag componentView.layer.transform = newTransform; } } - if (props[@"opacity"] && componentView.layer.opacity != (float)newViewProps.opacity) { + if (updatesOpacity && componentView.layer.opacity != (float)newViewProps.opacity) { componentView.layer.opacity = newViewProps.opacity; } diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.h b/packages/react-native/React/Fabric/RCTSurfacePresenter.h index f6bc2cc4d0a10f..a6796cf1802d47 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.h +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.h @@ -66,6 +66,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable RCTFabricSurface *)surfaceForRootTag:(ReactTag)rootTag; - (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props; +- (void)schedulerDidSynchronouslyUpdateViewOnUIThread:(ReactTag)tag props:(folly::dynamic)props; - (void)setupAnimationDriverWithSurfaceHandler:(const facebook::react::SurfaceHandler &)surfaceHandler; diff --git a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm index 48f9410a8cab46..1e675096b3121d 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePresenter.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePresenter.mm @@ -149,13 +149,18 @@ - (UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag } - (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props +{ + ReactTag tag = [reactTag integerValue]; + [self schedulerDidSynchronouslyUpdateViewOnUIThread:tag props:convertIdToFollyDynamic(props)]; +} + +- (void)schedulerDidSynchronouslyUpdateViewOnUIThread:(ReactTag)tag props:(folly::dynamic)props { RCTScheduler *scheduler = [self scheduler]; if (!scheduler) { return; } - ReactTag tag = [reactTag integerValue]; UIView *componentView = [_mountingManager.componentViewRegistry findComponentViewWithTag:tag]; if (componentView == nil) { @@ -168,7 +173,9 @@ - (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictiona return; } - [_mountingManager synchronouslyUpdateViewOnUIThread:tag changedProps:props componentDescriptor:*componentDescriptor]; + [_mountingManager synchronouslyUpdateViewOnUIThread:tag + changedProps:std::move(props) + componentDescriptor:*componentDescriptor]; } - (void)setupAnimationDriverWithSurfaceHandler:(const facebook::react::SurfaceHandler &)surfaceHandler diff --git a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.h b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.h index 3ec64dccad104d..0967daba2342da 100644 --- a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.h +++ b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.h @@ -12,6 +12,7 @@ namespace facebook::react { folly::dynamic convertIdToFollyDynamic(id json); -id convertFollyDynamicToId(const folly::dynamic& dyn); +id convertFollyDynamicToId(const folly::dynamic &dyn); +NSArray *extractKeysFromFollyDynamic(const folly::dynamic &dyn); } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.mm b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.mm index c1860e89fe9d8f..fe42ba85b80476 100644 --- a/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.mm +++ b/packages/react-native/ReactCommon/react/utils/platform/ios/react/utils/FollyConvert.mm @@ -113,4 +113,20 @@ id convertFollyDynamicToId(const folly::dynamic &dyn) return nil; } +NSArray *extractKeysFromFollyDynamic(const folly::dynamic &dyn) +{ + NSMutableArray *result = [NSMutableArray new]; + + if (dyn.type() == folly::dynamic::OBJECT) { + for (const auto &elem : dyn.items()) { + NSString *key = [[NSString alloc] initWithBytes:elem.first.c_str() + length:elem.first.size() + encoding:NSUTF8StringEncoding]; + [result addObject:key]; + } + } + + return result; +} + } // namespace facebook::react From 084382f5e6ba655e992fcd881654345ceefc848a Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Fri, 16 May 2025 08:16:12 -0700 Subject: [PATCH 3/4] implement SchedulerDelegateProxy::schedulerShouldSynchronouslyUpdateViewOnUIThread Differential Revision: D74739294 --- packages/react-native/React/Fabric/RCTScheduler.h | 1 + packages/react-native/React/Fabric/RCTScheduler.mm | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-native/React/Fabric/RCTScheduler.h b/packages/react-native/React/Fabric/RCTScheduler.h index f5df6a938c98cf..ed585390890b1d 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.h +++ b/packages/react-native/React/Fabric/RCTScheduler.h @@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN blockNativeResponder:(BOOL)blockNativeResponder forShadowView:(const facebook::react::ShadowView &)shadowView; +- (void)schedulerDidSynchronouslyUpdateViewOnUIThread:(facebook::react::Tag)reactTag props:(folly::dynamic)props; @end /** diff --git a/packages/react-native/React/Fabric/RCTScheduler.mm b/packages/react-native/React/Fabric/RCTScheduler.mm index 28450b2fc29c22..b8efdebfecc472 100644 --- a/packages/react-native/React/Fabric/RCTScheduler.mm +++ b/packages/react-native/React/Fabric/RCTScheduler.mm @@ -68,8 +68,8 @@ void schedulerDidSendAccessibilityEvent(const ShadowView &shadowView, const std: void schedulerShouldSynchronouslyUpdateViewOnUIThread(facebook::react::Tag tag, const folly::dynamic &props) override { - // Does nothing. - // This delegate method is not currently used on iOS. + RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; + [scheduler.delegate schedulerDidSynchronouslyUpdateViewOnUIThread:tag props:props]; } private: From 206f6a582b8fd0bc5a9d3938587d2072a48d1406 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Fri, 16 May 2025 08:23:40 -0700 Subject: [PATCH 4/4] use C++ Native Animated from JS if enabled (#51337) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51337 changelog: [internal] When C++ Native Animated is used, we can't be using TurboModuleAnimated native module. This diff just add the check to make sure that if C++ Native Animated is used, it will be correctly referenced from JS. Reviewed By: lenaic Differential Revision: D74490166 --- .../Libraries/Animated/shouldUseTurboAnimatedModule.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js b/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js index 0a780617e48309..2758cc734699c3 100644 --- a/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js +++ b/packages/react-native/Libraries/Animated/shouldUseTurboAnimatedModule.js @@ -8,10 +8,15 @@ * @format */ +import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags'; import Platform from '../Utilities/Platform'; function shouldUseTurboAnimatedModule(): boolean { - return Platform.OS === 'ios' && global.RN$Bridgeless === true; + if (ReactNativeFeatureFlags.cxxNativeAnimatedEnabled()) { + return false; + } else { + return Platform.OS === 'ios' && global.RN$Bridgeless === true; + } } export default shouldUseTurboAnimatedModule;