From 5133ef2022d9b03ba49a93f128d5b94a54956c79 Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Thu, 5 Oct 2023 12:30:24 +0200 Subject: [PATCH 1/4] Fix race-condition during render --- apple/LayoutReanimation/REASwizzledUIManager.h | 1 + .../LayoutReanimation/REASwizzledUIManager.mm | 18 ++++++++++++++++++ apple/REANodesManager.mm | 6 +++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/apple/LayoutReanimation/REASwizzledUIManager.h b/apple/LayoutReanimation/REASwizzledUIManager.h index 515edd6847c8..e755e3bee701 100644 --- a/apple/LayoutReanimation/REASwizzledUIManager.h +++ b/apple/LayoutReanimation/REASwizzledUIManager.h @@ -1,6 +1,7 @@ #import @interface REASwizzledUIManager : NSObject +- (void)setIsViewTreesSynchronized:(NSNumber *)isViewTreesSynchronized; - (instancetype)initWithUIManager:(RCTUIManager *)uiManager withAnimationManager:(REAAnimationsManager *)animationsManager; @end diff --git a/apple/LayoutReanimation/REASwizzledUIManager.mm b/apple/LayoutReanimation/REASwizzledUIManager.mm index fad0ca4c08c0..c5c1c4defec0 100644 --- a/apple/LayoutReanimation/REASwizzledUIManager.mm +++ b/apple/LayoutReanimation/REASwizzledUIManager.mm @@ -9,6 +9,7 @@ #import @interface RCTUIManager (Reanimated) +@property (atomic) NSNumber *isViewTreesSynchronized; @property REAAnimationsManager *animationsManager; - (NSArray> *)_childrenToRemoveFromContainer:(id)container atIndices:(NSArray *)atIndices; @@ -24,6 +25,16 @@ - (id)animationsManager { return objc_getAssociatedObject(self, @selector(animationsManager)); } + +@dynamic isViewTreesSynchronized; +- (void)setIsViewTreesSynchronized:(NSNumber *)isViewTreesSynchronized +{ + objc_setAssociatedObject(self, @selector(isViewTreesSynchronized), isViewTreesSynchronized, OBJC_ASSOCIATION_RETAIN); +} +- (id)isViewTreesSynchronized +{ + return objc_getAssociatedObject(self, @selector(isViewTreesSynchronized)); +} @end @implementation REASwizzledUIManager @@ -33,6 +44,7 @@ - (instancetype)initWithUIManager:(RCTUIManager *)uiManager { if (self = [super init]) { [uiManager setAnimationsManager:animationsManager]; + [uiManager setIsViewTreesSynchronized:@(1)]; [self swizzleMethods]; } return self; @@ -66,6 +78,12 @@ - (void)reanimated_manageChildren:(NSNumber *)containerTag removeAtIndices:(NSArray *)removeAtIndices registry:(NSMutableDictionary> *)registry { + BOOL isUIViewRegistry = ((id)registry == (id)[self valueForKey:@"_viewRegistry"]); + if (!isUIViewRegistry) { + [self setIsViewTreesSynchronized:@(1)]; + } else { + [self setIsViewTreesSynchronized:@(0)]; + } bool isLayoutAnimationEnabled = reanimated::FeaturesConfig::isLayoutAnimationEnabled(); id container; NSArray> *permanentlyRemovedChildren; diff --git a/apple/REANodesManager.mm b/apple/REANodesManager.mm index a3a533adf306..d564919f1c00 100644 --- a/apple/REANodesManager.mm +++ b/apple/REANodesManager.mm @@ -34,6 +34,8 @@ - (void)updateView:(nonnull NSNumber *)reactTag viewName:(NSString *)viewName pr - (void)setNeedsLayout; +- (id)isViewTreesSynchronized; + @end @interface RCTUIManager (SyncUpdates) @@ -336,8 +338,10 @@ - (void)performOperations if (strongSelf == nil) { return; } - BOOL canUpdateSynchronously = trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands]; + auto isViewTreesSynchronized = [[strongSelf.uiManager isViewTreesSynchronized] intValue] == 1; + BOOL canUpdateSynchronously = + trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands] && !isViewTreesSynchronized; if (!canUpdateSynchronously) { [syncUpdateObserver unblockUIThread]; } From dd000977836925a828c205382311d64dff1d48a5 Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Thu, 5 Oct 2023 12:40:06 +0200 Subject: [PATCH 2/4] Add comment --- apple/LayoutReanimation/REASwizzledUIManager.mm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apple/LayoutReanimation/REASwizzledUIManager.mm b/apple/LayoutReanimation/REASwizzledUIManager.mm index c5c1c4defec0..fb7405dd0735 100644 --- a/apple/LayoutReanimation/REASwizzledUIManager.mm +++ b/apple/LayoutReanimation/REASwizzledUIManager.mm @@ -37,7 +37,9 @@ - (id)isViewTreesSynchronized } @end -@implementation REASwizzledUIManager +@implementation REASwizzledUIManager { + NSMutableDictionary *_viewRegistry; +} - (instancetype)initWithUIManager:(RCTUIManager *)uiManager withAnimationManager:(REAAnimationsManager *)animationsManager @@ -78,8 +80,11 @@ - (void)reanimated_manageChildren:(NSNumber *)containerTag removeAtIndices:(NSArray *)removeAtIndices registry:(NSMutableDictionary> *)registry { - BOOL isUIViewRegistry = ((id)registry == (id)[self valueForKey:@"_viewRegistry"]); + BOOL isUIViewRegistry = (id)registry == (id)_viewRegistry; if (!isUIViewRegistry) { + // The `isViewTreesSynchronized` flag is necessary to prevent Reanimated from performing + // synchronous updates when there is inconsistency between the shadow tree and native tree. + // More details here: https://github.com/software-mansion/react-native-reanimated/pull/5187 [self setIsViewTreesSynchronized:@(1)]; } else { [self setIsViewTreesSynchronized:@(0)]; From 52b8549edd09ffd031ac9bd41e6261dabac8e6ad Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Thu, 5 Oct 2023 14:04:50 +0200 Subject: [PATCH 3/4] Use REAUIKit --- apple/LayoutReanimation/REASwizzledUIManager.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/LayoutReanimation/REASwizzledUIManager.mm b/apple/LayoutReanimation/REASwizzledUIManager.mm index fb7405dd0735..03ce0463d843 100644 --- a/apple/LayoutReanimation/REASwizzledUIManager.mm +++ b/apple/LayoutReanimation/REASwizzledUIManager.mm @@ -38,7 +38,7 @@ - (id)isViewTreesSynchronized @end @implementation REASwizzledUIManager { - NSMutableDictionary *_viewRegistry; + NSMutableDictionary *_viewRegistry; } - (instancetype)initWithUIManager:(RCTUIManager *)uiManager From f9d44b9fb47e2dcb4279c0881fc569e1cac9e96b Mon Sep 17 00:00:00 2001 From: Krzysztof Piaskowy Date: Thu, 5 Oct 2023 14:39:45 +0200 Subject: [PATCH 4/4] Remove negation --- apple/LayoutReanimation/REASwizzledUIManager.mm | 4 ++-- apple/REANodesManager.mm | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apple/LayoutReanimation/REASwizzledUIManager.mm b/apple/LayoutReanimation/REASwizzledUIManager.mm index 03ce0463d843..a98c5071aab6 100644 --- a/apple/LayoutReanimation/REASwizzledUIManager.mm +++ b/apple/LayoutReanimation/REASwizzledUIManager.mm @@ -85,9 +85,9 @@ - (void)reanimated_manageChildren:(NSNumber *)containerTag // The `isViewTreesSynchronized` flag is necessary to prevent Reanimated from performing // synchronous updates when there is inconsistency between the shadow tree and native tree. // More details here: https://github.com/software-mansion/react-native-reanimated/pull/5187 - [self setIsViewTreesSynchronized:@(1)]; - } else { [self setIsViewTreesSynchronized:@(0)]; + } else { + [self setIsViewTreesSynchronized:@(1)]; } bool isLayoutAnimationEnabled = reanimated::FeaturesConfig::isLayoutAnimationEnabled(); id container; diff --git a/apple/REANodesManager.mm b/apple/REANodesManager.mm index d564919f1c00..5e2c27eb0a4e 100644 --- a/apple/REANodesManager.mm +++ b/apple/REANodesManager.mm @@ -341,7 +341,7 @@ - (void)performOperations auto isViewTreesSynchronized = [[strongSelf.uiManager isViewTreesSynchronized] intValue] == 1; BOOL canUpdateSynchronously = - trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands] && !isViewTreesSynchronized; + trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands] && isViewTreesSynchronized; if (!canUpdateSynchronously) { [syncUpdateObserver unblockUIThread]; }