Skip to content

Commit 7f22db8

Browse files
dmytrorykunfacebook-github-bot
authored andcommitted
Introduce __nativeComponentRegistry__getNativeViewConfig (#37522)
Summary: Pull Request resolved: #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
1 parent da3dcd7 commit 7f22db8

File tree

11 files changed

+199
-20
lines changed

11 files changed

+199
-20
lines changed

packages/react-native/React/Base/RCTConstants.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,9 @@ RCT_EXTERN void RCTSetMemoryPressureUnloadLevel(int value);
8181
*/
8282
RCT_EXTERN BOOL RCTGetParseUnhandledJSErrorStackNatively(void);
8383
RCT_EXTERN void RCTSetParseUnhandledJSErrorStackNatively(BOOL value);
84+
85+
/*
86+
* Use native view configs in bridgeless mode
87+
*/
88+
RCT_EXTERN BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void);
89+
RCT_EXTERN void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value);

packages/react-native/React/Base/RCTConstants.m

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,18 @@ void RCTSetParseUnhandledJSErrorStackNatively(BOOL value)
8181
{
8282
RCTParseUnhandledJSErrorStackNatively = value;
8383
}
84+
85+
/*
86+
* Use native view configs in bridgeless mode
87+
*/
88+
static BOOL RCTUseNativeViewConfigsInBridgelessMode = NO;
89+
90+
BOOL RCTGetUseNativeViewConfigsInBridgelessMode(void)
91+
{
92+
return RCTUseNativeViewConfigsInBridgelessMode;
93+
}
94+
95+
void RCTSetUseNativeViewConfigsInBridgelessMode(BOOL value)
96+
{
97+
RCTUseNativeViewConfigsInBridgelessMode = value;
98+
}

packages/react-native/React/Modules/RCTUIManager.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,10 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier
172172
@property (nonatomic, readonly) RCTUIManager *uiManager;
173173

174174
@end
175+
176+
RCT_EXTERN NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
177+
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
178+
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
179+
Class managerClass,
180+
NSString *name,
181+
NSDictionary<NSString *, id> *viewConfig);

packages/react-native/React/Modules/RCTUIManager.m

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,10 +1463,12 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
14631463
}];
14641464
}
14651465

1466-
static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
1466+
NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
14671467
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
14681468
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
1469-
RCTComponentData *componentData)
1469+
Class managerClass,
1470+
NSString *name,
1471+
NSDictionary<NSString *, id> *viewConfig)
14701472
{
14711473
NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
14721474

@@ -1476,10 +1478,9 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
14761478
NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];
14771479

14781480
// Add manager class
1479-
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
1481+
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(managerClass);
14801482

14811483
// Add native props
1482-
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
14831484
moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
14841485
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
14851486
moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
@@ -1497,7 +1498,7 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
14971498
RCTLogError(
14981499
@"Component '%@' re-registered bubbling event '%@' as a "
14991500
"direct event",
1500-
componentData.name,
1501+
name,
15011502
eventName);
15021503
}
15031504
}
@@ -1518,7 +1519,7 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
15181519
RCTLogError(
15191520
@"Component '%@' re-registered direct event '%@' as a "
15201521
"bubbling event",
1521-
componentData.name,
1522+
name,
15221523
eventName);
15231524
}
15241525
}
@@ -1540,14 +1541,23 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
15401541
RCTLogError(
15411542
@"Component '%@' re-registered direct event '%@' as a "
15421543
"bubbling event",
1543-
componentData.name,
1544+
name,
15441545
eventName);
15451546
}
15461547
}
15471548

15481549
return moduleConstants;
15491550
}
15501551

1552+
static NSMutableDictionary<NSString *, id> *moduleConstantsForComponentData(
1553+
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
1554+
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
1555+
RCTComponentData *componentData)
1556+
{
1557+
return RCTModuleConstantsForDestructuredComponent(
1558+
directEvents, bubblingEvents, componentData.managerClass, componentData.name, componentData.viewConfig);
1559+
}
1560+
15511561
- (NSDictionary<NSString *, id> *)constantsToExport
15521562
{
15531563
return [self getConstants];
@@ -1563,7 +1573,7 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
15631573
enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
15641574
RCTAssert(!constants[name], @"UIManager already has constants for %@", componentData.name);
15651575
NSMutableDictionary<NSString *, id> *moduleConstants =
1566-
moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
1576+
moduleConstantsForComponentData(directEvents, bubblingEvents, componentData);
15671577
constants[name] = moduleConstants;
15681578
}];
15691579

@@ -1611,7 +1621,7 @@ static void RCTMeasureLayout(RCTShadowView *view, RCTShadowView *ancestor, RCTRe
16111621
NSMutableDictionary *directEvents = [NSMutableDictionary new];
16121622
NSMutableDictionary *bubblingEvents = [NSMutableDictionary new];
16131623
NSMutableDictionary<NSString *, id> *moduleConstants =
1614-
moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
1624+
moduleConstantsForComponentData(directEvents, bubblingEvents, componentData);
16151625
return @{
16161626
@"viewConfig" : moduleConstants,
16171627
};

packages/react-native/React/Views/RCTComponentData.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ NS_ASSUME_NONNULL_BEGIN
4242
@property (nonatomic, copy, nullable) void (^eventInterceptor)
4343
(NSString *eventName, NSDictionary *event, NSNumber *reactTag);
4444

45+
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass;
4546
- (NSDictionary<NSString *, id> *)viewConfig;
4647

4748
@end
4849

50+
RCT_EXTERN NSString *RCTViewManagerModuleNameForClass(Class managerClass);
51+
4952
NS_ASSUME_NONNULL_END

packages/react-native/React/Views/RCTComponentData.m

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ - (instancetype)initWithManagerClass:(Class)managerClass
5555
_viewPropBlocks = [NSMutableDictionary new];
5656
_shadowPropBlocks = [NSMutableDictionary new];
5757

58-
_name = moduleNameForClass(managerClass);
58+
_name = RCTViewManagerModuleNameForClass(managerClass);
5959
}
6060
return self;
6161
}
@@ -385,16 +385,16 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
385385
}];
386386
}
387387

388-
- (NSDictionary<NSString *, id> *)viewConfig
388+
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass
389389
{
390390
NSMutableArray<NSString *> *bubblingEvents = [NSMutableArray new];
391391
NSMutableArray<NSString *> *capturingEvents = [NSMutableArray new];
392392
NSMutableArray<NSString *> *directEvents = [NSMutableArray new];
393393

394394
#pragma clang diagnostic push
395395
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
396-
if (RCTClassOverridesInstanceMethod(_managerClass, @selector(customBubblingEventTypes))) {
397-
NSArray<NSString *> *events = [self.manager customBubblingEventTypes];
396+
if (RCTClassOverridesInstanceMethod(managerClass, @selector(customBubblingEventTypes))) {
397+
NSArray<NSString *> *events = [[managerClass new] customBubblingEventTypes];
398398
for (NSString *event in events) {
399399
[bubblingEvents addObject:RCTNormalizeInputEventName(event)];
400400
}
@@ -403,7 +403,7 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
403403

404404
unsigned int count = 0;
405405
NSMutableDictionary *propTypes = [NSMutableDictionary new];
406-
Method *methods = class_copyMethodList(object_getClass(_managerClass), &count);
406+
Method *methods = class_copyMethodList(object_getClass(managerClass), &count);
407407
for (unsigned int i = 0; i < count; i++) {
408408
SEL selector = method_getName(methods[i]);
409409
const char *selectorName = sel_getName(selector);
@@ -418,13 +418,13 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
418418
}
419419

420420
NSString *name = @(underscorePos + 1);
421-
NSString *type = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(_managerClass, selector)[0];
421+
NSString *type = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(managerClass, selector)[0];
422422
if (RCT_DEBUG && propTypes[name] && ![propTypes[name] isEqualToString:type]) {
423423
RCTLogError(
424424
@"Property '%@' of component '%@' redefined from '%@' "
425425
"to '%@'",
426426
name,
427-
_name,
427+
RCTViewManagerModuleNameForClass(managerClass),
428428
propTypes[name],
429429
type);
430430
}
@@ -450,24 +450,31 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
450450
RCTLogError(
451451
@"Component '%@' registered '%@' as both a bubbling event "
452452
"and a direct event",
453-
_name,
453+
RCTViewManagerModuleNameForClass(managerClass),
454454
event);
455455
}
456456
}
457457
#endif
458458

459-
Class superClass = [_managerClass superclass];
459+
Class superClass = [managerClass superclass];
460460

461461
return @{
462462
@"propTypes" : propTypes,
463463
@"directEvents" : directEvents,
464464
@"bubblingEvents" : bubblingEvents,
465465
@"capturingEvents" : capturingEvents,
466-
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : moduleNameForClass(superClass),
466+
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : RCTViewManagerModuleNameForClass(superClass),
467467
};
468468
}
469469

470-
static NSString *moduleNameForClass(Class managerClass)
470+
- (NSDictionary<NSString *, id> *)viewConfig
471+
{
472+
// Make sure the manager is initialized before accessing view config.
473+
[self manager];
474+
return [RCTComponentData viewConfigForViewMangerClass:_managerClass];
475+
}
476+
477+
NSString *RCTViewManagerModuleNameForClass(Class managerClass)
471478
{
472479
// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
473480
// We want to get rid of RCT and RK prefixes, but a lot of JS code still references
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "NativeViewConfigProviderBinding.h"
9+
10+
namespace facebook::react::NativeViewConfigProviderBinding {
11+
12+
void install(jsi::Runtime &runtime, ProviderType &&provider) {
13+
auto name = "RN$NativeComponentRegistry_getNativeViewConfig";
14+
auto hostFunction = [provider = std::move(provider)](
15+
jsi::Runtime &runtime,
16+
jsi::Value const & /*thisValue*/,
17+
jsi::Value const *args,
18+
size_t count) -> jsi::Value {
19+
if (count != 1 || !args[0].isString()) {
20+
throw new jsi::JSError(runtime, "1 argument of type String expected.");
21+
}
22+
return provider(args[0].getString(runtime).utf8(runtime));
23+
};
24+
25+
auto jsiFunction = jsi::Function::createFromHostFunction(
26+
runtime, jsi::PropNameID::forAscii(runtime, name), 2, hostFunction);
27+
28+
runtime.global().setProperty(runtime, name, jsiFunction);
29+
}
30+
} // namespace facebook::react::NativeViewConfigProviderBinding
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <jsi/jsi.h>
11+
12+
namespace facebook::react::NativeViewConfigProviderBinding {
13+
14+
using ProviderType = std::function<jsi::Value(const std::string &name)>;
15+
16+
/*
17+
* Installs native view config provider into JavaScript runtime.
18+
*/
19+
void install(jsi::Runtime &runtime, ProviderType &&provider);
20+
} // namespace facebook::react::NativeViewConfigProviderBinding

packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#import <React/RCTBridgeModule.h>
1515
#import <React/RCTBridgeModuleDecorator.h>
1616
#import <React/RCTComponentViewFactory.h>
17+
#import <React/RCTConstants.h>
1718
#import <React/RCTCxxUtils.h>
1819
#import <React/RCTDevSettings.h>
1920
#import <React/RCTDisplayLink.h>
@@ -25,6 +26,7 @@
2526
#import <React/RCTModuleData.h>
2627
#import <React/RCTPerformanceLogger.h>
2728
#import <React/RCTSurfacePresenter.h>
29+
#import <ReactCommon/RCTNativeViewConfigProvider.h>
2830
#import <ReactCommon/RCTTurboModuleManager.h>
2931
#import <ReactCommon/RuntimeExecutor.h>
3032
#import <cxxreact/ReactMarker.h>
@@ -283,6 +285,10 @@ - (void)_start
283285
});
284286
RCTInstallNativeComponentRegistryBinding(runtime);
285287

288+
if (RCTGetUseNativeViewConfigsInBridgelessMode()) {
289+
installNativeViewConfigProviderBinding(runtime);
290+
}
291+
286292
if (strongSelf->_bindingsInstallFunc) {
287293
strongSelf->_bindingsInstallFunc(runtime);
288294
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <jsi/jsi.h>
11+
12+
namespace facebook::react {
13+
/*
14+
* Installs native view config provider into JavaScript runtime.
15+
*/
16+
void installNativeViewConfigProviderBinding(jsi::Runtime &runtime);
17+
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "RCTNativeViewConfigProvider.h"
9+
10+
#import <React/RCTBridge+Private.h>
11+
#import <React/RCTComponentData.h>
12+
#import <React/RCTUIManager.h>
13+
#import <React/RCTViewManager.h>
14+
#import <ReactCommon/RCTTurboModule.h>
15+
#import <react/bridgeless/nativeviewconfig/NativeViewConfigProviderBinding.h>
16+
17+
namespace facebook::react {
18+
namespace {
19+
20+
// This function eagerly loads module constants for every RCTViewManager subclass.
21+
// This is not compatible with lazily loaded modules, but we don't have them in OSS, so that's fine for now.
22+
NSDictionary<NSString *, NSObject *> *eagerViewConfigs()
23+
{
24+
static NSMutableDictionary<NSString *, NSObject *> *result = [NSMutableDictionary new];
25+
static dispatch_once_t onceToken;
26+
dispatch_once(&onceToken, ^{
27+
auto directEvents = [NSMutableDictionary new];
28+
auto bubblingEvents = [NSMutableDictionary new];
29+
for (Class moduleClass in RCTGetModuleClasses()) {
30+
if ([moduleClass isSubclassOfClass:RCTViewManager.class]) {
31+
auto name = RCTViewManagerModuleNameForClass(moduleClass);
32+
auto viewConfig = [RCTComponentData viewConfigForViewMangerClass:moduleClass];
33+
auto moduleConstants =
34+
RCTModuleConstantsForDestructuredComponent(directEvents, bubblingEvents, moduleClass, name, viewConfig);
35+
result[name] = moduleConstants;
36+
}
37+
}
38+
});
39+
return result;
40+
}
41+
42+
jsi::Value provideNativeViewConfig(facebook::jsi::Runtime &runtime, std::string const &name)
43+
{
44+
auto componentName = [NSString stringWithCString:name.c_str() encoding:NSASCIIStringEncoding];
45+
auto viewConfig = eagerViewConfigs()[componentName];
46+
return TurboModuleConvertUtils::convertObjCObjectToJSIValue(runtime, viewConfig);
47+
};
48+
49+
} // namespace
50+
51+
void installNativeViewConfigProviderBinding(jsi::Runtime &runtime)
52+
{
53+
auto nativeViewConfigProvider = [&runtime](std::string const &name) -> jsi::Value {
54+
return provideNativeViewConfig(runtime, name);
55+
};
56+
NativeViewConfigProviderBinding::install(runtime, std::move(nativeViewConfigProvider));
57+
}
58+
} // namespace facebook::react

0 commit comments

Comments
 (0)