Skip to content

Commit 40c1d68

Browse files
committed
[Bridge] RCT_EXPORT_NAMED_METHOD(js_name, selector)
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros. The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`). Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
1 parent 0c7c923 commit 40c1d68

File tree

2 files changed

+44
-18
lines changed

2 files changed

+44
-18
lines changed

React/Base/RCTBridge.m

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,19 @@ @implementation RCTModuleMethod
224224
return methodName;
225225
}
226226

227-
- (instancetype)initWithMethodName:(NSString *)methodName
228-
JSMethodName:(NSString *)JSMethodName
227+
- (instancetype)initLegacyWithMethodName:(NSString *)methodName
228+
JSMethodName:(NSString *)JSMethodName
229+
{
230+
return [self initWithReactMethodName:methodName objCMethodName:methodName JSMethodName:JSMethodName];
231+
}
232+
233+
- (instancetype)initWithReactMethodName:(NSString *)reactMethodName
234+
objCMethodName:(NSString *)objCMethodName
235+
JSMethodName:(NSString *)JSMethodName
229236
{
230237
if ((self = [super init])) {
231-
_methodName = methodName;
232-
NSArray *parts = [[methodName substringWithRange:(NSRange){2, methodName.length - 3}] componentsSeparatedByString:@" "];
238+
_methodName = reactMethodName;
239+
NSArray *parts = [[reactMethodName substringWithRange:(NSRange){2, reactMethodName.length - 3}] componentsSeparatedByString:@" "];
233240

234241
// Parse class and method
235242
_moduleClassName = parts[0];
@@ -243,7 +250,7 @@ - (instancetype)initWithMethodName:(NSString *)methodName
243250
// New format
244251
NSString *selectorString = [parts[1] substringFromIndex:14];
245252
_selector = NSSelectorFromString(selectorString);
246-
_JSMethodName = RCTStringUpToFirstArgument(selectorString);
253+
_JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
247254

248255
static NSRegularExpression *regExp;
249256
if (!regExp) {
@@ -255,8 +262,8 @@ - (instancetype)initWithMethodName:(NSString *)methodName
255262
}
256263

257264
argumentNames = [NSMutableArray array];
258-
[regExp enumerateMatchesInString:JSMethodName options:0 range:NSMakeRange(0, JSMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
259-
NSString *argumentName = [JSMethodName substringWithRange:[result rangeAtIndex:1]];
265+
[regExp enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
266+
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
260267
[(NSMutableArray *)argumentNames addObject:argumentName];
261268
}];
262269
} else {
@@ -267,14 +274,14 @@ - (instancetype)initWithMethodName:(NSString *)methodName
267274
}
268275

269276
// Extract class and method details
270-
_isClassMethod = [methodName characterAtIndex:0] == '+';
277+
_isClassMethod = [reactMethodName characterAtIndex:0] == '+';
271278
_moduleClass = NSClassFromString(_moduleClassName);
272279

273280
#if DEBUG
274281
// Sanity check
275282
RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
276283
@"You are attempting to export the method %@, but %@ does not \
277-
conform to the RCTBridgeModule Protocol", methodName, _moduleClassName);
284+
conform to the RCTBridgeModule Protocol", objCMethodName, _moduleClassName);
278285
#endif
279286

280287
// Get method signature
@@ -493,15 +500,21 @@ - (NSString *)description
493500

494501
for (RCTHeaderValue addr = section->offset;
495502
addr < section->offset + section->size;
496-
addr += sizeof(const char **) * 2) {
503+
addr += sizeof(const char **) * 3) {
497504

498505
// Get data entry
499506
const char **entries = (const char **)(mach_header + addr);
500507

501508
// Create method
502-
RCTModuleMethod *moduleMethod =
503-
[[RCTModuleMethod alloc] initWithMethodName:@(entries[0])
504-
JSMethodName:strlen(entries[1]) ? @(entries[1]) : nil];
509+
RCTModuleMethod *moduleMethod;
510+
if (entries[2] == NULL) {
511+
moduleMethod = [[RCTModuleMethod alloc] initLegacyWithMethodName:@(entries[0])
512+
JSMethodName:strlen(entries[1]) ? @(entries[1]) : nil];
513+
} else {
514+
moduleMethod = [[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
515+
objCMethodName:strlen(entries[1]) ? @(entries[1]) : nil
516+
JSMethodName:strlen(entries[2]) ? @(entries[2]) : nil];
517+
}
505518

506519
// Cache method
507520
NSArray *methods = methodsByModuleClassName[moduleMethod.moduleClassName];

React/Base/RCTBridgeModule.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,14 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
5151
#define RCT_EXPORT(js_name) \
5252
_Pragma("message(\"RCT_EXPORT is deprecated. Use RCT_EXPORT_METHOD instead.\")") \
5353
__attribute__((used, section("__DATA,RCTExport"))) \
54-
static const char *__rct_export_entry__[] = { __func__, #js_name }
54+
__attribute__((__aligned__(1))) \
55+
static const char *__rct_export_entry__[] = { __func__, #js_name, NULL }
5556

5657
/**
5758
* Wrap the parameter line of your method implementation with this macro to
58-
* expose it to JS. Unlike the deprecated RCT_EXPORT, this macro does not take
59-
* a js_name argument and the exposed method will match the first part of the
60-
* Objective-C method selector name (up to the first colon).
59+
* expose it to JS. By default the exposed method will match the first part of
60+
* the Objective-C method selector name (up to the first colon). Use
61+
* RCT_EXPORT_NAMED_METHOD to specify the JS name of the method.
6162
*
6263
* For example, in MyClass.m:
6364
*
@@ -74,9 +75,21 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
7475
* and is exposed to JavaScript as `NativeModules.ModuleName.doSomething`.
7576
*/
7677
#define RCT_EXPORT_METHOD(method) \
78+
RCT_EXPORT_NAMED_METHOD(, method)
79+
80+
/**
81+
* Similar to RCT_EXPORT_METHOD but lets you set the JS name of the exported
82+
* method. Example usage:
83+
*
84+
* RCT_EXPORT_NAMED_METHOD(setURL,
85+
* setURLString:(NSString *)urlString)
86+
* {}
87+
*/
88+
#define RCT_EXPORT_NAMED_METHOD(js_name, method) \
7789
- (void)__rct_export__##method { \
7890
__attribute__((used, section("__DATA,RCTExport"))) \
79-
static const char *__rct_export_entry__[] = { __func__, #method }; \
91+
__attribute__((__aligned__(1))) \
92+
static const char *__rct_export_entry__[] = { __func__, #method, #js_name }; \
8093
} \
8194
- (void)method
8295

0 commit comments

Comments
 (0)