diff --git a/packages/react-native/React/Base/RCTBridgeProxy.mm b/packages/react-native/React/Base/RCTBridgeProxy.mm index af6b7afb687ac7..6c1e914c2f86b7 100644 --- a/packages/react-native/React/Base/RCTBridgeProxy.mm +++ b/packages/react-native/React/Base/RCTBridgeProxy.mm @@ -414,8 +414,9 @@ - (UIView *)viewForReactTag:(NSNumber *)reactTag { [self logWarning:@"Please migrate to RCTViewRegistry: @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED." cmd:_cmd]; - return [_viewRegistry viewForReactTag:reactTag] ? [_viewRegistry viewForReactTag:reactTag] - : [_legacyViewRegistry objectForKey:reactTag]; + UIView *view = [_viewRegistry viewForReactTag:reactTag] ? [_viewRegistry viewForReactTag:reactTag] + : [_legacyViewRegistry objectForKey:reactTag]; + return [RCTUIManager paperViewOrCurrentView:view]; } - (void)addUIBlock:(RCTViewManagerUIBlock)block @@ -428,7 +429,11 @@ - (void)addUIBlock:(RCTViewManagerUIBlock)block RCTExecuteOnMainQueue(^{ __typeof(self) strongSelf = weakSelf; if (strongSelf) { - block((RCTUIManager *)strongSelf, strongSelf->_legacyViewRegistry); + RCTUIManager *proxiedManager = (RCTUIManager *)strongSelf; + RCTComposedViewRegistry *composedViewRegistry = + [[RCTComposedViewRegistry alloc] initWithUIManager:proxiedManager + andRegistry:strongSelf->_legacyViewRegistry]; + block(proxiedManager, composedViewRegistry); } }); } diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.h index fae91e1b10d4b9..9ad322f0d54880 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.h +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.h @@ -21,6 +21,12 @@ NS_ASSUME_NONNULL_BEGIN + (void)supportLegacyViewManagerWithName:(NSString *)componentName; + (void)supportLegacyViewManagersWithPrefix:(NSString *)prefix; +/** + * This method is required for addUIBlock and to let the infra bypass the interop layer + * when providing views from the RCTUIManager. The interop layer should be transparent to the users. + */ +- (UIView *)paperView; + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm index d3f4abfb771511..4dd1f7782c9dce 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm @@ -263,6 +263,11 @@ - (void)_setPropsWithUpdateMask:(RNComponentViewUpdateMask)updateMask } } +- (UIView *)paperView +{ + return _adapter.paperView; +} + #pragma mark - Native Commands - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropCoordinatorAdapter.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropCoordinatorAdapter.mm index 5470c02b935a6e..341de93e877f63 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropCoordinatorAdapter.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropCoordinatorAdapter.mm @@ -26,7 +26,6 @@ - (instancetype)initWithCoordinator:(RCTLegacyViewManagerInteropCoordinator *)co - (void)dealloc { - [_coordinator removeViewFromRegistryWithTag:_tag]; [_paperView removeFromSuperview]; [_coordinator removeObserveForTag:_tag]; } @@ -42,7 +41,6 @@ - (UIView *)paperView weakSelf.eventInterceptor(eventName, event); } }]; - [_coordinator addViewToRegistry:_paperView withTag:_tag]; } return _paperView; } diff --git a/packages/react-native/React/Modules/RCTUIManager.h b/packages/react-native/React/Modules/RCTUIManager.h index 0979c965f8fdca..fbd8e2c166cdbf 100644 --- a/packages/react-native/React/Modules/RCTUIManager.h +++ b/packages/react-native/React/Modules/RCTUIManager.h @@ -155,6 +155,14 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier */ - (void)setNeedsLayout; +/** + * This method is used to extract the wrapped view + * from a view that might be the Interop Layer view wrapper. + * If the view passed as parameter is the Interop Layer wrapper, this method returns the wrapped view + * Otherwise, it returns the view itself. + */ ++ (UIView *)paperViewOrCurrentView:(UIView *)view; + /** * Dedicated object for subscribing for UIManager events. * See `RCTUIManagerObserver` protocol for more details. @@ -180,6 +188,30 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier @end +/** + * This is a composed ViewRegistry which implement the same behavior of a Dictionary. + * We need this because, when libraries use `addUIBlock` they receives both the UIManager and a Dictionary which maps + * reactTags to Views. The problem is that in the New Architecture that dictionary is always empty and many libraries + * broke because they want to access the dictionary. Instead, they should use the `uiManager viewForReactTag` method to + * retrieve the views they need. + * + * The `RCTComposedViewRegistry` follows the composite pattern and receives as inputs the `RCTUIManager`and the + * dictionary that was passed to the libraries. It extends `NSDictionary` to make sure that it has the same interface of + * the parameter that is passed. This class fixes the problem because we override the`objectForKeyedSubscript:` method + * which is used to access the dictionary. That method is now implemented looking in the dictionary registry first and + * then using the`viewForReactTag` method. + */ +@interface RCTComposedViewRegistry : NSMutableDictionary + +- (instancetype)initWithUIManager:(RCTUIManager *)uiManager andRegistry:(NSDictionary *)registry; + +@end + +// This protocol is needed to silence the "unknown selector" warning +@protocol RCTRendererInteropLayerAdapting +- (UIView *)paperView; +@end + RCT_EXTERN NSMutableDictionary *RCTModuleConstantsForDestructuredComponent( NSMutableDictionary *directEvents, NSMutableDictionary *bubblingEvents, diff --git a/packages/react-native/React/Modules/RCTUIManager.m b/packages/react-native/React/Modules/RCTUIManager.m index 1d85520d0a4a78..f5e5c7c7a82aca 100644 --- a/packages/react-native/React/Modules/RCTUIManager.m +++ b/packages/react-native/React/Modules/RCTUIManager.m @@ -374,7 +374,7 @@ - (UIView *)viewForReactTag:(NSNumber *)reactTag if (!view) { view = _viewRegistry[reactTag]; } - return view; + return [RCTUIManager paperViewOrCurrentView:view]; } - (RCTShadowView *)shadowViewForReactTag:(NSNumber *)reactTag @@ -1157,7 +1157,9 @@ - (void)flushUIBlocksWithCompletion:(void (^)(void))completion @try { for (RCTViewManagerUIBlock block in previousPendingUIBlocks) { - block(strongSelf, strongSelf->_viewRegistry); + RCTComposedViewRegistry *composedViewRegistry = + [[RCTComposedViewRegistry alloc] initWithUIManager:strongSelf andRegistry:strongSelf->_viewRegistry]; + block(strongSelf, composedViewRegistry); } } @catch (NSException *exception) { RCTLogError(@"Exception thrown while executing UI block: %@", exception); @@ -1639,6 +1641,19 @@ + (UIView *)JSResponder return _jsResponder; } ++ (UIView *)paperViewOrCurrentView:(UIView *)view +{ + if ([view respondsToSelector:@selector(paperView)]) { + return [view performSelector:@selector(paperView)]; + } + return view; +} + +- (void)removeViewFromRegistry:(NSNumber *)reactTag +{ + [_viewRegistry removeObjectForKey:reactTag]; +} + @end @implementation RCTBridge (RCTUIManager) @@ -1649,3 +1664,55 @@ - (RCTUIManager *)uiManager } @end + +@implementation RCTComposedViewRegistry { + __weak RCTUIManager *_uiManager; + NSDictionary *_registry; +} + +- (instancetype)initWithUIManager:(RCTUIManager *)uiManager andRegistry:(NSDictionary *)registry +{ + self = [super init]; + if (self) { + self->_uiManager = uiManager; + self->_registry = registry; + } + return self; +} + +- (id)objectForKey:(id)key +{ + if (![key isKindOfClass:[NSNumber class]]) { + return [super objectForKeyedSubscript:key]; + } + + NSNumber *index = (NSNumber *)key; + UIView *view = [_uiManager viewForReactTag:index]; + if (view) { + return [RCTUIManager paperViewOrCurrentView:view]; + } + view = _registry[index]; + if (view) { + return [RCTUIManager paperViewOrCurrentView:view]; + } + return [super objectForKeyedSubscript:key]; +} + +- (void)removeObjectForKey:(id)key +{ + if (![key isKindOfClass:[NSNumber class]]) { + return [super removeObjectForKey:key]; + } + NSNumber *tag = (NSNumber *)key; + + if (_registry[key]) { + NSMutableDictionary *mutableRegistry = (NSMutableDictionary *)_registry; + [mutableRegistry removeObjectForKey:tag]; + } else if ([_uiManager viewForReactTag:tag]) { + [_uiManager removeViewFromRegistry:tag]; + } else { + [super removeObjectForKey:key]; + } +} + +@end diff --git a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h index 1a39901d1e2f41..e0b1a969de22ad 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h +++ b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.h @@ -39,11 +39,6 @@ typedef void (^InterceptorBlock)(std::string eventName, folly::dynamic event); args:(NSArray *)args reactTag:(NSInteger)tag paperView:(UIView *)paperView; - -- (void)removeViewFromRegistryWithTag:(NSInteger)tag; - -- (void)addViewToRegistry:(UIView *)view withTag:(NSInteger)tag; - @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index ea8222b2e81218..bb5720e122d16d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -147,31 +147,6 @@ - (void)handleCommand:(NSString *)commandName } } -- (void)addViewToRegistry:(UIView *)view withTag:(NSInteger)tag -{ - [self _addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - if ([viewRegistry objectForKey:@(tag)] != NULL) { - return; - } - NSMutableDictionary *mutableViewRegistry = - (NSMutableDictionary *)viewRegistry; - [mutableViewRegistry setObject:view forKey:@(tag)]; - }]; -} - -- (void)removeViewFromRegistryWithTag:(NSInteger)tag -{ - [self _addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - if ([viewRegistry objectForKey:@(tag)] == NULL) { - return; - } - - NSMutableDictionary *mutableViewRegistry = - (NSMutableDictionary *)viewRegistry; - [mutableViewRegistry removeObjectForKey:@(tag)]; - }]; -} - #pragma mark - Private - (void)_handleCommandsOnBridge:(id)method withArgs:(NSArray *)newArgs {