Skip to content

Commit a125c84

Browse files
Bartlomiej Bloniarzmeta-codesync[bot]
authored andcommitted
Add direct AnimatedProps update path for iOS
Summary: Add an iOS-optimized code path that passes typed AnimatedProps directly through the delegate chain, avoiding the folly::dynamic serialization round-trip. Android and CxxPlatform get no-op stubs. Gated behind the `optimizedAnimatedPropUpdates` feature flag. Changelog: [iOS][Added] - Add a direct AnimatedProps update path through `RCTScheduler` / `RCTMountingManager` that bypasses the folly::dynamic serialization round-trip (gated by `optimizedAnimatedPropUpdates`) Differential Revision: D101157449
1 parent bd58c71 commit a125c84

11 files changed

Lines changed: 109 additions & 7 deletions

packages/react-native/React/Fabric/Mounting/RCTMountingManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ NS_ASSUME_NONNULL_BEGIN
6969
- (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag
7070
changedProps:(folly::dynamic)props
7171
componentDescriptor:(const facebook::react::ComponentDescriptor &)componentDescriptor;
72+
73+
- (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag withProps:(facebook::react::Props::Shared)newProps;
7274
@end
7375

7476
NS_ASSUME_NONNULL_END

packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,41 @@ - (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag
330330
[componentView finalizeUpdates:RNComponentViewUpdateMaskProps];
331331
}
332332

333+
- (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag withProps:(Props::Shared)newProps
334+
{
335+
RCTAssertMainQueue();
336+
337+
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry findComponentViewWithTag:reactTag];
338+
if (!componentView) {
339+
RCTLogWarn(@"Attempted to update view with tag %ld, but it no longer exists", (long)reactTag);
340+
return;
341+
}
342+
343+
NSSet<NSString *> *propKeys = componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN ?: [NSSet new];
344+
345+
Props::Shared oldProps = [componentView props];
346+
347+
componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil;
348+
[componentView updateProps:newProps oldProps:oldProps];
349+
componentView.propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = propKeys;
350+
351+
const auto &newViewProps = static_cast<const ViewProps &>(*newProps);
352+
353+
if (componentView.layer.opacity != (float)newViewProps.opacity) {
354+
componentView.layer.opacity = newViewProps.opacity;
355+
}
356+
357+
auto layoutMetrics = LayoutMetrics();
358+
layoutMetrics.frame.size.width = componentView.layer.bounds.size.width;
359+
layoutMetrics.frame.size.height = componentView.layer.bounds.size.height;
360+
CATransform3D newTransform = RCTCATransform3DFromTransformMatrix(newViewProps.resolveTransform(layoutMetrics));
361+
if (!CATransform3DEqualToTransform(newTransform, componentView.layer.transform)) {
362+
componentView.layer.transform = newTransform;
363+
}
364+
365+
[componentView finalizeUpdates:RNComponentViewUpdateMaskProps];
366+
}
367+
333368
- (void)synchronouslyDispatchCommandOnUIThread:(ReactTag)reactTag
334369
commandName:(NSString *)commandName
335370
args:(NSArray *)args

packages/react-native/React/Fabric/RCTScheduler.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#import <react/renderer/uimanager/UIManager.h>
2020
#import <react/utils/ContextContainer.h>
2121

22+
namespace facebook::react {
23+
struct AnimatedProps;
24+
} // namespace facebook::react
25+
2226
NS_ASSUME_NONNULL_BEGIN
2327

2428
/**
@@ -45,6 +49,11 @@ NS_ASSUME_NONNULL_BEGIN
4549
forShadowView:(const facebook::react::ShadowView &)shadowView;
4650

4751
- (void)schedulerDidSynchronouslyUpdateViewOnUIThread:(facebook::react::Tag)reactTag props:(folly::dynamic)props;
52+
53+
- (void)schedulerDidSynchronouslyUpdateViewWithAnimatedPropsOnUIThread:(facebook::react::Tag)reactTag
54+
surfaceId:(facebook::react::SurfaceId)surfaceId
55+
animatedProps:
56+
(const facebook::react::AnimatedProps &)animatedProps;
4857
@end
4958

5059
/**

packages/react-native/React/Fabric/RCTScheduler.mm

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import <QuartzCore/CADisplayLink.h>
1111
#import <cxxreact/TraceSection.h>
1212
#import <react/featureflags/ReactNativeFeatureFlags.h>
13+
#import <react/renderer/animationbackend/AnimatedProps.h>
1314
#import <react/renderer/animations/LayoutAnimationDriver.h>
1415
#import <react/renderer/componentregistry/ComponentDescriptorFactory.h>
1516
#import <react/renderer/scheduler/Scheduler.h>
@@ -83,7 +84,12 @@ void schedulerShouldSynchronouslyUpdateAnimatedPropsOnUIThread(
8384
SurfaceId surfaceId,
8485
const std::unordered_map<Tag, AnimatedProps> &updates) override
8586
{
86-
// Not implemented on iOS yet -- filled in by a later commit.
87+
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
88+
for (const auto &[tag, animatedProps] : updates) {
89+
[scheduler.delegate schedulerDidSynchronouslyUpdateViewWithAnimatedPropsOnUIThread:tag
90+
surfaceId:surfaceId
91+
animatedProps:animatedProps];
92+
}
8793
}
8894

8995
void schedulerDidUpdateShadowTree(const std::unordered_map<Tag, folly::dynamic> &tagToProps) override

packages/react-native/React/Fabric/RCTSurfacePresenter.mm

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
#import <react/utils/FollyConvert.h>
2929

3030
#import <react/featureflags/ReactNativeFeatureFlags.h>
31+
#import <react/renderer/animationbackend/AnimatedProps.h>
3132
#import <react/renderer/componentregistry/ComponentDescriptorFactory.h>
3233
#import <react/renderer/components/text/BaseTextProps.h>
34+
#import <react/renderer/components/view/BaseViewProps.h>
3335
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
3436
#import <react/renderer/scheduler/SchedulerToolbox.h>
3537
#import <react/utils/ContextContainer.h>
@@ -416,6 +418,44 @@ - (void)schedulerDidSetIsJSResponder:(BOOL)isJSResponder
416418
[_mountingManager setIsJSResponder:isJSResponder blockNativeResponder:blockNativeResponder forShadowView:shadowView];
417419
}
418420

421+
- (void)schedulerDidSynchronouslyUpdateViewWithAnimatedPropsOnUIThread:(Tag)tag
422+
surfaceId:(SurfaceId)surfaceId
423+
animatedProps:(const AnimatedProps &)animatedProps
424+
{
425+
RCTScheduler *scheduler = [self scheduler];
426+
if (!scheduler) {
427+
return;
428+
}
429+
430+
UIView<RCTComponentViewProtocol> *componentView =
431+
[_mountingManager.componentViewRegistry findComponentViewWithTag:tag];
432+
if (componentView == nil) {
433+
return;
434+
}
435+
ComponentHandle handle = [[componentView class] componentDescriptorProvider].handle;
436+
auto *componentDescriptor = [scheduler findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN:handle];
437+
if (!componentDescriptor) {
438+
return;
439+
}
440+
441+
Props::Shared oldProps = [componentView props];
442+
PropsParserContext propsParserContext{surfaceId, *[self contextContainer]};
443+
444+
Props::Shared newProps;
445+
if (animatedProps.rawProps) {
446+
newProps = componentDescriptor->cloneProps(propsParserContext, oldProps, RawProps(*animatedProps.rawProps));
447+
} else {
448+
newProps = componentDescriptor->cloneProps(propsParserContext, oldProps, RawProps{});
449+
}
450+
451+
auto viewProps = std::const_pointer_cast<BaseViewProps>(std::static_pointer_cast<const BaseViewProps>(newProps));
452+
for (auto &animatedProp : animatedProps.props) {
453+
cloneProp(*viewProps, *animatedProp);
454+
}
455+
456+
[_mountingManager synchronouslyUpdateViewOnUIThread:tag withProps:newProps];
457+
}
458+
419459
- (void)addObserver:(id<RCTSurfacePresenterObserver>)observer
420460
{
421461
std::unique_lock lock(_observerListMutex);

scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,8 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima
15601560
public virtual void trigger() override;
15611561
public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates);
15621562
public void requestAsyncFlushForSurfaces(const std::set<facebook::react::SurfaceId>& surfaces);
1563-
public void synchronouslyUpdateProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
1563+
public void synchronouslyUpdateProps(facebook::react::SurfaceId surfaceId, const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
1564+
public void synchronouslyUpdatePropsUnbuffered(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
15641565
}
15651566

15661567
class facebook::react::AnimationBackendCommitHook : public facebook::react::UIManagerCommitHook {

scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,8 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima
15591559
public virtual void trigger() override;
15601560
public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates);
15611561
public void requestAsyncFlushForSurfaces(const std::set<facebook::react::SurfaceId>& surfaces);
1562-
public void synchronouslyUpdateProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
1562+
public void synchronouslyUpdateProps(facebook::react::SurfaceId surfaceId, const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
1563+
public void synchronouslyUpdatePropsUnbuffered(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
15631564
}
15641565

15651566
class facebook::react::AnimationBackendCommitHook : public facebook::react::UIManagerCommitHook {

scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,7 @@ interface RCTMountingManager : public NSObject {
15951595
public virtual void setContextContainer:(std::shared_ptr<const facebook::react::ContextContainer> contextContainer);
15961596
public virtual void setIsJSResponder:blockNativeResponder:forShadowView:(BOOL isJSResponder, BOOL blockNativeResponder, const facebook::react::ShadowView& shadowView);
15971597
public virtual void synchronouslyUpdateViewOnUIThread:changedProps:componentDescriptor:(ReactTag reactTag, folly::dynamic props, const facebook::react::ComponentDescriptor& componentDescriptor);
1598+
public virtual void synchronouslyUpdateViewOnUIThread:withProps:(ReactTag reactTag, facebook::react::Props::Shared newProps);
15981599
}
15991600

16001601
interface RCTMultilineTextInputView : public RCTBaseTextInputView {
@@ -3387,6 +3388,7 @@ protocol RCTSchedulerDelegate {
33873388
public virtual void schedulerDidSendAccessibilityEvent:eventType:(const facebook::react::ShadowView& shadowView, const std::string& eventType);
33883389
public virtual void schedulerDidSetIsJSResponder:blockNativeResponder:forShadowView:(BOOL isJSResponder, BOOL blockNativeResponder, const facebook::react::ShadowView& shadowView);
33893390
public virtual void schedulerDidSynchronouslyUpdateViewOnUIThread:props:(facebook::react::Tag reactTag, folly::dynamic props);
3391+
public virtual void schedulerDidSynchronouslyUpdateViewWithAnimatedPropsOnUIThread:surfaceId:animatedProps:(facebook::react::Tag reactTag, facebook::react::SurfaceId surfaceId, const facebook::react::AnimatedProps& animatedProps);
33903392
public virtual void schedulerShouldMergeReactRevision:(facebook::react::SurfaceId surfaceId);
33913393
public virtual void schedulerShouldRenderTransactions:(std::shared_ptr<const facebook::react::MountingCoordinator> mountingCoordinator);
33923394
}
@@ -4535,7 +4537,8 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima
45354537
public virtual void trigger() override;
45364538
public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates);
45374539
public void requestAsyncFlushForSurfaces(const std::set<facebook::react::SurfaceId>& surfaces);
4538-
public void synchronouslyUpdateProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
4540+
public void synchronouslyUpdateProps(facebook::react::SurfaceId surfaceId, const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
4541+
public void synchronouslyUpdatePropsUnbuffered(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
45394542
}
45404543

45414544
class facebook::react::AnimationBackendCommitHook : public facebook::react::UIManagerCommitHook {

scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,7 @@ interface RCTMountingManager : public NSObject {
15951595
public virtual void setContextContainer:(std::shared_ptr<const facebook::react::ContextContainer> contextContainer);
15961596
public virtual void setIsJSResponder:blockNativeResponder:forShadowView:(BOOL isJSResponder, BOOL blockNativeResponder, const facebook::react::ShadowView& shadowView);
15971597
public virtual void synchronouslyUpdateViewOnUIThread:changedProps:componentDescriptor:(ReactTag reactTag, folly::dynamic props, const facebook::react::ComponentDescriptor& componentDescriptor);
1598+
public virtual void synchronouslyUpdateViewOnUIThread:withProps:(ReactTag reactTag, facebook::react::Props::Shared newProps);
15981599
}
15991600

16001601
interface RCTMultilineTextInputView : public RCTBaseTextInputView {
@@ -3387,6 +3388,7 @@ protocol RCTSchedulerDelegate {
33873388
public virtual void schedulerDidSendAccessibilityEvent:eventType:(const facebook::react::ShadowView& shadowView, const std::string& eventType);
33883389
public virtual void schedulerDidSetIsJSResponder:blockNativeResponder:forShadowView:(BOOL isJSResponder, BOOL blockNativeResponder, const facebook::react::ShadowView& shadowView);
33893390
public virtual void schedulerDidSynchronouslyUpdateViewOnUIThread:props:(facebook::react::Tag reactTag, folly::dynamic props);
3391+
public virtual void schedulerDidSynchronouslyUpdateViewWithAnimatedPropsOnUIThread:surfaceId:animatedProps:(facebook::react::Tag reactTag, facebook::react::SurfaceId surfaceId, const facebook::react::AnimatedProps& animatedProps);
33903392
public virtual void schedulerShouldMergeReactRevision:(facebook::react::SurfaceId surfaceId);
33913393
public virtual void schedulerShouldRenderTransactions:(std::shared_ptr<const facebook::react::MountingCoordinator> mountingCoordinator);
33923394
}
@@ -4534,7 +4536,8 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima
45344536
public virtual void trigger() override;
45354537
public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates);
45364538
public void requestAsyncFlushForSurfaces(const std::set<facebook::react::SurfaceId>& surfaces);
4537-
public void synchronouslyUpdateProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
4539+
public void synchronouslyUpdateProps(facebook::react::SurfaceId surfaceId, const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
4540+
public void synchronouslyUpdatePropsUnbuffered(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
45384541
}
45394542

45404543
class facebook::react::AnimationBackendCommitHook : public facebook::react::UIManagerCommitHook {

scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,8 @@ class facebook::react::AnimationBackend : public facebook::react::UIManagerAnima
885885
public virtual void trigger() override;
886886
public void commitUpdates(facebook::react::SurfaceId surfaceId, facebook::react::SurfaceUpdates& surfaceUpdates);
887887
public void requestAsyncFlushForSurfaces(const std::set<facebook::react::SurfaceId>& surfaces);
888-
public void synchronouslyUpdateProps(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
888+
public void synchronouslyUpdateProps(facebook::react::SurfaceId surfaceId, const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
889+
public void synchronouslyUpdatePropsUnbuffered(const std::unordered_map<facebook::react::Tag, facebook::react::AnimatedProps>& updates);
889890
}
890891

891892
class facebook::react::AnimationBackendCommitHook : public facebook::react::UIManagerCommitHook {

0 commit comments

Comments
 (0)