Introduce __nativeComponentRegistry__getNativeViewConfig (#37522)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/37522

This diff adds cross-platform Cxx binding helper and iOS specific implementation of `__nativeComponentRegistry__getNativeViewConfig` to JS runtime. This function provides native view config for a native component in bridgeless mode.

Changelog:
[Internal] - Introduce `__nativeComponentRegistry__getNativeViewConfig` in iOS.

Reviewed By: javache

Differential Revision: D43538804

fbshipit-source-id: 0aeca40c1c2a625eca5d60e466eb24df30453ba7
This commit is contained in:
Dmitry Rykun
2023-05-30 08:04:23 -07:00
committed by Facebook GitHub Bot
parent da3dcd7326
commit 7f22db8ea0
11 changed files with 199 additions and 20 deletions
@@ -81,3 +81,9 @@ RCT_EXTERN void RCTSetMemoryPressureUnloadLevel(int value);
*/
RCT_EXTERN BOOL RCTGetParseUnhandledJSErrorStackNatively(void);
RCT_EXTERN void RCTSetParseUnhandledJSErrorStackNatively(BOOL value);
/*
* Use native view configs in bridgeless mode
*/
RCT_EXTERN BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void);
RCT_EXTERN void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value);
@@ -81,3 +81,18 @@ void RCTSetParseUnhandledJSErrorStackNatively(BOOL value)
{
RCTParseUnhandledJSErrorStackNatively = value;
}
/*
* Use native view configs in bridgeless mode
*/
static BOOL RCTUseNativeViewConfigsInBridgelessMode = NO;
BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void)
{
return RCTUseNativeViewConfigsInBridgelessMode;
}
void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value)
{
RCTUseNativeViewConfigsInBridgelessMode = value;
}
@@ -172,3 +172,10 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier
@property (nonatomic, readonly) RCTUIManager *uiManager;
@end
RCT_EXTERN NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
Class managerClass,
NSString *name,
NSDictionary<NSString *, id> *viewConfig);
@@ -1463,10 +1463,12 @@ RCT_EXPORT_METHOD(clearJSResponder)
}];
}
static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
RCTComponentData *componentData)
Class managerClass,
NSString *name,
NSDictionary<NSString *, id> *viewConfig)
{
NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
@@ -1476,10 +1478,9 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];
// Add manager class
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(managerClass);
// Add native props
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
@@ -1497,7 +1498,7 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
RCTLogError(
@"Component '%@' re-registered bubbling event '%@' as a "
"direct event",
componentData.name,
name,
eventName);
}
}
@@ -1518,7 +1519,7 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
RCTLogError(
@"Component '%@' re-registered direct event '%@' as a "
"bubbling event",
componentData.name,
name,
eventName);
}
}
@@ -1540,7 +1541,7 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
RCTLogError(
@"Component '%@' re-registered direct event '%@' as a "
"bubbling event",
componentData.name,
name,
eventName);
}
}
@@ -1548,6 +1549,15 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
return moduleConstants;
}
static NSMutableDictionary<NSString *, id> *moduleConstantsForComponentData(
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
RCTComponentData *componentData)
{
return RCTModuleConstantsForDestructuredComponent(
directEvents, bubblingEvents, componentData.managerClass, componentData.name, componentData.viewConfig);
}
- (NSDictionary<NSString *, id> *)constantsToExport
{
return [self getConstants];
@@ -1563,7 +1573,7 @@ static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
RCTAssert(!constants[name], @"UIManager already has constants for %@", componentData.name);
NSMutableDictionary<NSString *, id> *moduleConstants =
moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
moduleConstantsForComponentData(directEvents, bubblingEvents, componentData);
constants[name] = moduleConstants;
}];
@@ -1611,7 +1621,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(lazilyLoadView : (NSString *)name)
NSMutableDictionary *directEvents = [NSMutableDictionary new];
NSMutableDictionary *bubblingEvents = [NSMutableDictionary new];
NSMutableDictionary<NSString *, id> *moduleConstants =
moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
moduleConstantsForComponentData(directEvents, bubblingEvents, componentData);
return @{
@"viewConfig" : moduleConstants,
};
@@ -42,8 +42,11 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy, nullable) void (^eventInterceptor)
(NSString *eventName, NSDictionary *event, NSNumber *reactTag);
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass;
- (NSDictionary<NSString *, id> *)viewConfig;
@end
RCT_EXTERN NSString *RCTViewManagerModuleNameForClass(Class managerClass);
NS_ASSUME_NONNULL_END
@@ -55,7 +55,7 @@ static SEL selectorForType(NSString *type)
_viewPropBlocks = [NSMutableDictionary new];
_shadowPropBlocks = [NSMutableDictionary new];
_name = moduleNameForClass(managerClass);
_name = RCTViewManagerModuleNameForClass(managerClass);
}
return self;
}
@@ -385,7 +385,7 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
}];
}
- (NSDictionary<NSString *, id> *)viewConfig
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass
{
NSMutableArray<NSString *> *bubblingEvents = [NSMutableArray new];
NSMutableArray<NSString *> *capturingEvents = [NSMutableArray new];
@@ -393,8 +393,8 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (RCTClassOverridesInstanceMethod(_managerClass, @selector(customBubblingEventTypes))) {
NSArray<NSString *> *events = [self.manager customBubblingEventTypes];
if (RCTClassOverridesInstanceMethod(managerClass, @selector(customBubblingEventTypes))) {
NSArray<NSString *> *events = [[managerClass new] customBubblingEventTypes];
for (NSString *event in events) {
[bubblingEvents addObject:RCTNormalizeInputEventName(event)];
}
@@ -403,7 +403,7 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
unsigned int count = 0;
NSMutableDictionary *propTypes = [NSMutableDictionary new];
Method *methods = class_copyMethodList(object_getClass(_managerClass), &count);
Method *methods = class_copyMethodList(object_getClass(managerClass), &count);
for (unsigned int i = 0; i < count; i++) {
SEL selector = method_getName(methods[i]);
const char *selectorName = sel_getName(selector);
@@ -418,13 +418,13 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
}
NSString *name = @(underscorePos + 1);
NSString *type = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(_managerClass, selector)[0];
NSString *type = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(managerClass, selector)[0];
if (RCT_DEBUG && propTypes[name] && ![propTypes[name] isEqualToString:type]) {
RCTLogError(
@"Property '%@' of component '%@' redefined from '%@' "
"to '%@'",
name,
_name,
RCTViewManagerModuleNameForClass(managerClass),
propTypes[name],
type);
}
@@ -450,24 +450,31 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
RCTLogError(
@"Component '%@' registered '%@' as both a bubbling event "
"and a direct event",
_name,
RCTViewManagerModuleNameForClass(managerClass),
event);
}
}
#endif
Class superClass = [_managerClass superclass];
Class superClass = [managerClass superclass];
return @{
@"propTypes" : propTypes,
@"directEvents" : directEvents,
@"bubblingEvents" : bubblingEvents,
@"capturingEvents" : capturingEvents,
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : moduleNameForClass(superClass),
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : RCTViewManagerModuleNameForClass(superClass),
};
}
static NSString *moduleNameForClass(Class managerClass)
- (NSDictionary<NSString *, id> *)viewConfig
{
// Make sure the manager is initialized before accessing view config.
[self manager];
return [RCTComponentData viewConfigForViewMangerClass:_managerClass];
}
NSString *RCTViewManagerModuleNameForClass(Class managerClass)
{
// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
// We want to get rid of RCT and RK prefixes, but a lot of JS code still references
@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "NativeViewConfigProviderBinding.h"
namespace facebook::react::NativeViewConfigProviderBinding {
void install(jsi::Runtime &runtime, ProviderType &&provider) {
auto name = "RN$NativeComponentRegistry_getNativeViewConfig";
auto hostFunction = [provider = std::move(provider)](
jsi::Runtime &runtime,
jsi::Value const & /*thisValue*/,
jsi::Value const *args,
size_t count) -> jsi::Value {
if (count != 1 || !args[0].isString()) {
throw new jsi::JSError(runtime, "1 argument of type String expected.");
}
return provider(args[0].getString(runtime).utf8(runtime));
};
auto jsiFunction = jsi::Function::createFromHostFunction(
runtime, jsi::PropNameID::forAscii(runtime, name), 2, hostFunction);
runtime.global().setProperty(runtime, name, jsiFunction);
}
} // namespace facebook::react::NativeViewConfigProviderBinding
@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jsi/jsi.h>
namespace facebook::react::NativeViewConfigProviderBinding {
using ProviderType = std::function<jsi::Value(const std::string &name)>;
/*
* Installs native view config provider into JavaScript runtime.
*/
void install(jsi::Runtime &runtime, ProviderType &&provider);
} // namespace facebook::react::NativeViewConfigProviderBinding
@@ -14,6 +14,7 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTBridgeModuleDecorator.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTConstants.h>
#import <React/RCTCxxUtils.h>
#import <React/RCTDevSettings.h>
#import <React/RCTDisplayLink.h>
@@ -25,6 +26,7 @@
#import <React/RCTModuleData.h>
#import <React/RCTPerformanceLogger.h>
#import <React/RCTSurfacePresenter.h>
#import <ReactCommon/RCTNativeViewConfigProvider.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <ReactCommon/RuntimeExecutor.h>
#import <cxxreact/ReactMarker.h>
@@ -283,6 +285,10 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
});
RCTInstallNativeComponentRegistryBinding(runtime);
if (RCTGetUseNativeViewConfigsInBridgelessMode()) {
installNativeViewConfigProviderBinding(runtime);
}
if (strongSelf->_bindingsInstallFunc) {
strongSelf->_bindingsInstallFunc(runtime);
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jsi/jsi.h>
namespace facebook::react {
/*
* Installs native view config provider into JavaScript runtime.
*/
void installNativeViewConfigProviderBinding(jsi::Runtime &runtime);
} // namespace facebook::react
@@ -0,0 +1,58 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTNativeViewConfigProvider.h"
#import <React/RCTBridge+Private.h>
#import <React/RCTComponentData.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>
#import <ReactCommon/RCTTurboModule.h>
#import <react/bridgeless/nativeviewconfig/NativeViewConfigProviderBinding.h>
namespace facebook::react {
namespace {
// This function eagerly loads module constants for every RCTViewManager subclass.
// This is not compatible with lazily loaded modules, but we don't have them in OSS, so that's fine for now.
NSDictionary<NSString *, NSObject *> *eagerViewConfigs()
{
static NSMutableDictionary<NSString *, NSObject *> *result = [NSMutableDictionary new];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
auto directEvents = [NSMutableDictionary new];
auto bubblingEvents = [NSMutableDictionary new];
for (Class moduleClass in RCTGetModuleClasses()) {
if ([moduleClass isSubclassOfClass:RCTViewManager.class]) {
auto name = RCTViewManagerModuleNameForClass(moduleClass);
auto viewConfig = [RCTComponentData viewConfigForViewMangerClass:moduleClass];
auto moduleConstants =
RCTModuleConstantsForDestructuredComponent(directEvents, bubblingEvents, moduleClass, name, viewConfig);
result[name] = moduleConstants;
}
}
});
return result;
}
jsi::Value provideNativeViewConfig(facebook::jsi::Runtime &runtime, std::string const &name)
{
auto componentName = [NSString stringWithCString:name.c_str() encoding:NSASCIIStringEncoding];
auto viewConfig = eagerViewConfigs()[componentName];
return TurboModuleConvertUtils::convertObjCObjectToJSIValue(runtime, viewConfig);
};
} // namespace
void installNativeViewConfigProviderBinding(jsi::Runtime &runtime)
{
auto nativeViewConfigProvider = [&runtime](std::string const &name) -> jsi::Value {
return provideNativeViewConfig(runtime, name);
};
NativeViewConfigProviderBinding::install(runtime, std::move(nativeViewConfigProvider));
}
} // namespace facebook::react