iOS: Add Commands and Constants properties to native view config (#38221)

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

View configs are supposed to have `Commands` and `Constants`. See [ReactNativeTypes.js](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Renderer/shims/ReactNativeTypes.js#L68-L69).
Android sets them in native: see [UIManagerModuleConstantsHelper.java](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java#L172-L179).
ut iOS doesn't: see [RCTUIManager.m](https://github.com/facebook/react-native/blob/main/packages/react-native/React/Modules/RCTUIManager.m#L1484-L1487)
Instead there is code for that in [PaperUIManager.js](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/ReactNative/PaperUIManager.js#L117-L160).
It accesses viewManagers like this: `const viewManager = NativeModules[viewConfig.Manager]`. But `NativeModules` object is not available in the bridgeless mode. So we fail to provide complete native view configs in the New Architecture.

This diff implements `Commands` and `Constants` in native in iOS.
This change should have no effect in the old architecture because these properties are overwritten by `lazifyViewManagerConfig`.

Changelog: [Internal] - Add Commands and Constants properties to native view config.

Reviewed By: sammy-SC

Differential Revision: D47096624

fbshipit-source-id: e3b3183ba5e3d1d2fb3f3ff5d6ff89ad86095a6c
This commit is contained in:
Dmitry Rykun
2023-07-18 08:15:13 -07:00
committed by Facebook GitHub Bot
parent 080a3d300d
commit 47557ae781
2 changed files with 41 additions and 0 deletions
@@ -1485,6 +1485,13 @@ NSMutableDictionary<NSString *, id> *RCTModuleConstantsForDestructuredComponent(
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
moduleConstants[@"directEventTypes"] = directEventTypes;
// In the Old Architecture the "Commands" and "Constants" properties of view manager config are populated by
// lazifyViewManagerConfig function in JS. This fuction uses NativeModules global object that is not available in the
// New Architecture. To make native view configs work in the New Architecture we will populate these properties in
// native.
moduleConstants[@"Commands"] = viewConfig[@"Commands"];
// In the Old Architecture "Constants" are empty.
moduleConstants[@"Constants"] = [NSDictionary new];
// Add direct events
for (NSString *eventName in viewConfig[@"directEvents"]) {
@@ -15,6 +15,7 @@
#import "RCTConstants.h"
#import "RCTConvert.h"
#import "RCTEventDispatcherProtocol.h"
#import "RCTModuleMethod.h"
#import "RCTParserUtils.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"
@@ -385,6 +386,34 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
}];
}
+ (NSDictionary<NSString *, NSNumber *> *)commandsForViewMangerClass:(Class)managerClass
methods:(Method *)methods
methodCount:(unsigned int)methodCount
{
NSMutableDictionary<NSString *, NSNumber *> *commands = [NSMutableDictionary new];
static const char *prefix = "__rct_export__";
const unsigned int prefixLength = strlen(prefix);
int commandCount = 0;
for (int i = 0; i < methodCount; i++) {
SEL selector = method_getName(methods[i]);
const char *selectorName = sel_getName(selector);
if (strncmp(selectorName, prefix, prefixLength) != 0) {
continue;
}
RCTMethodInfo *methodInfo = ((RCTMethodInfo * (*)(id, SEL)) objc_msgSend)(managerClass, selector);
RCTModuleMethod *moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:methodInfo
moduleClass:managerClass];
NSString *methodName = @(moduleMethod.JSMethodName);
commands[methodName] = @(commandCount);
commandCount += 1;
}
// View manager do not export getConstants with RCT_EXPORT_METHOD, so we inject it into "Commands" manually.
if (commandCount > 0) {
commands[@"getConstants"] = @(commandCount);
}
return commands;
}
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass
{
NSMutableArray<NSString *> *bubblingEvents = [NSMutableArray new];
@@ -442,6 +471,10 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
propTypes[name] = type;
}
}
NSDictionary<NSString *, NSNumber *> *commands = [self commandsForViewMangerClass:managerClass
methods:methods
methodCount:count];
free(methods);
#if RCT_DEBUG
@@ -464,6 +497,7 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
@"bubblingEvents" : bubblingEvents,
@"capturingEvents" : capturingEvents,
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : RCTViewManagerModuleNameForClass(superClass),
@"Commands" : commands,
};
}