mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Generate objc handler for commands
Summary:
These functions will be called by components like this:
```
- (void)handleCommand:(NSString const *)commandName args:(NSArray const *)args
{
ViewNativeComponentHandleCommand(self, commandName, args);
}
```
Codegen currently supports commands with ints and bools as arguments. Will add more types in follow up diffs.
Reviewed By: JoshuaGross
Differential Revision: D16509123
fbshipit-source-id: c3071ce3b5da215bb8747216e57026a69a89eff0
This commit is contained in:
committed by
Facebook Github Bot
parent
47365da367
commit
88641f850a
+176
-1
@@ -11,6 +11,7 @@
|
||||
'use strict';
|
||||
|
||||
import type {
|
||||
CommandTypeShape,
|
||||
ComponentShape,
|
||||
SchemaType,
|
||||
CommandsFunctionTypeParamAnnotation,
|
||||
@@ -18,12 +19,69 @@ import type {
|
||||
|
||||
type FilesOutput = Map<string, string>;
|
||||
|
||||
function getOrdinalNumber(num: number): string {
|
||||
switch (num) {
|
||||
case 1:
|
||||
return '1st';
|
||||
case 2:
|
||||
return '2nd';
|
||||
case 3:
|
||||
return '3rd';
|
||||
}
|
||||
|
||||
if (num <= 20) {
|
||||
return `${num}th`;
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const protocolTemplate = `
|
||||
@protocol ::_COMPONENT_NAME_::ViewProtocol <NSObject>
|
||||
::_METHODS_::
|
||||
@end
|
||||
`.trim();
|
||||
|
||||
const commandHandlerIfCaseConvertArgTemplate = `
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg::_ARG_NUMBER_:: = args[::_ARG_NUMBER_::];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg::_ARG_NUMBER_::, ::_EXPECTED_KIND_::, @"::_EXPECTED_KIND_STRING_::", @"::_COMPONENT_NAME_::", commandName, @"::_ARG_NUMBER_STR_::")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
::_ARG_CONVERSION_::
|
||||
`.trim();
|
||||
|
||||
const commandHandlerIfCaseTemplate = `
|
||||
if ([commandName isEqualToString:@"::_COMMAND_NAME_::"]) {
|
||||
#if RCT_DEBUG
|
||||
if ([args count] != ::_NUM_ARGS_::) {
|
||||
RCTLogError(@"%@ command %@ received %d arguments, expected %d.", @"::_COMPONENT_NAME_::", commandName, (int)[args count], ::_NUM_ARGS_::);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
::_CONVERT_ARGS_::
|
||||
|
||||
::_COMMAND_CALL_::
|
||||
return;
|
||||
}
|
||||
`.trim();
|
||||
|
||||
const commandHandlerTemplate = `
|
||||
RCT_EXTERN inline void ::_COMPONENT_NAME_::HandleCommand(
|
||||
id<::_COMPONENT_NAME_::ViewProtocol> componentView,
|
||||
NSString const *commandName,
|
||||
NSArray const *args)
|
||||
{
|
||||
::_IF_CASES_::
|
||||
|
||||
#if RCT_DEBUG
|
||||
RCTLogError(@"%@ received command %@, which is not a supported command.", @"::_COMPONENT_NAME_::", commandName);
|
||||
#endif
|
||||
}
|
||||
`.trim();
|
||||
|
||||
const template = `
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
@@ -55,6 +113,49 @@ function getObjCParamType(param: CommandsFunctionTypeParamAnnotation): string {
|
||||
}
|
||||
}
|
||||
|
||||
function getObjCExpectedKindParamType(
|
||||
param: CommandsFunctionTypeParamAnnotation,
|
||||
): string {
|
||||
switch (param.typeAnnotation.type) {
|
||||
case 'BooleanTypeAnnotation':
|
||||
return '[NSNumber class]';
|
||||
case 'Int32TypeAnnotation':
|
||||
return '[NSNumber class]';
|
||||
default:
|
||||
(param.typeAnnotation.type: empty);
|
||||
throw new Error('Received invalid param type annotation');
|
||||
}
|
||||
}
|
||||
|
||||
function getReadableExpectedKindParamType(
|
||||
param: CommandsFunctionTypeParamAnnotation,
|
||||
): string {
|
||||
switch (param.typeAnnotation.type) {
|
||||
case 'BooleanTypeAnnotation':
|
||||
return 'boolean';
|
||||
case 'Int32TypeAnnotation':
|
||||
return 'number';
|
||||
default:
|
||||
(param.typeAnnotation.type: empty);
|
||||
throw new Error('Received invalid param type annotation');
|
||||
}
|
||||
}
|
||||
|
||||
function getObjCRightHandAssignmentParamType(
|
||||
param: CommandsFunctionTypeParamAnnotation,
|
||||
index: number,
|
||||
): string {
|
||||
switch (param.typeAnnotation.type) {
|
||||
case 'BooleanTypeAnnotation':
|
||||
return `[(NSNumber *)arg${index} boolValue]`;
|
||||
case 'Int32TypeAnnotation':
|
||||
return `[(NSNumber *)arg${index} intValue]`;
|
||||
default:
|
||||
(param.typeAnnotation.type: empty);
|
||||
throw new Error('Received invalid param type annotation');
|
||||
}
|
||||
}
|
||||
|
||||
function generateProtocol(
|
||||
component: ComponentShape,
|
||||
componentName: string,
|
||||
@@ -84,6 +185,75 @@ function generateProtocol(
|
||||
.replace('::_METHODS_::', commands);
|
||||
}
|
||||
|
||||
function generateConvertAndValidateParam(
|
||||
param: CommandsFunctionTypeParamAnnotation,
|
||||
index: number,
|
||||
componentName: string,
|
||||
): string {
|
||||
const leftSideType = getObjCParamType(param);
|
||||
const expectedKind = getObjCExpectedKindParamType(param);
|
||||
const expectedKindString = getReadableExpectedKindParamType(param);
|
||||
const argConversion = `${leftSideType} ${
|
||||
param.name
|
||||
} = ${getObjCRightHandAssignmentParamType(param, index)};`;
|
||||
|
||||
return commandHandlerIfCaseConvertArgTemplate
|
||||
.replace(/::_COMPONENT_NAME_::/g, componentName)
|
||||
.replace('::_ARG_CONVERSION_::', argConversion)
|
||||
.replace(/::_ARG_NUMBER_::/g, '' + index)
|
||||
.replace('::_ARG_NUMBER_STR_::', getOrdinalNumber(index + 1))
|
||||
.replace('::_EXPECTED_KIND_::', expectedKind)
|
||||
.replace('::_EXPECTED_KIND_STRING_::', expectedKindString);
|
||||
}
|
||||
|
||||
function generateCommandIfCase(
|
||||
command: CommandTypeShape,
|
||||
componentName: string,
|
||||
) {
|
||||
const params = command.typeAnnotation.params;
|
||||
|
||||
const convertArgs = params
|
||||
.map((param, index) =>
|
||||
generateConvertAndValidateParam(param, index, componentName),
|
||||
)
|
||||
.join('\n\n')
|
||||
.trim();
|
||||
|
||||
const commandCallArgs =
|
||||
params.length === 0
|
||||
? ''
|
||||
: params
|
||||
.map((param, index) => {
|
||||
return `${index === 0 ? '' : param.name}:${param.name}`;
|
||||
})
|
||||
.join(' ');
|
||||
const commandCall = `[componentView ${command.name}${commandCallArgs}]`;
|
||||
|
||||
return commandHandlerIfCaseTemplate
|
||||
.replace(/::_COMPONENT_NAME_::/g, componentName)
|
||||
.replace(/::_COMMAND_NAME_::/g, command.name)
|
||||
.replace(/::_NUM_ARGS_::/g, '' + params.length)
|
||||
.replace('::_CONVERT_ARGS_::', convertArgs)
|
||||
.replace('::_COMMAND_CALL_::', commandCall);
|
||||
}
|
||||
|
||||
function generateCommandHandler(
|
||||
component: ComponentShape,
|
||||
componentName: string,
|
||||
): ?string {
|
||||
if (component.commands.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ifCases = component.commands
|
||||
.map(command => generateCommandIfCase(command, componentName))
|
||||
.join('\n\n');
|
||||
|
||||
return commandHandlerTemplate
|
||||
.replace(/::_COMPONENT_NAME_::/g, componentName)
|
||||
.replace('::_IF_CASES_::', ifCases);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generate(libraryName: string, schema: SchemaType): FilesOutput {
|
||||
const fileName = 'ComponentViewHelpers.h';
|
||||
@@ -98,7 +268,12 @@ module.exports = {
|
||||
|
||||
return Object.keys(components)
|
||||
.map(componentName => {
|
||||
return generateProtocol(components[componentName], componentName);
|
||||
return [
|
||||
generateProtocol(components[componentName], componentName),
|
||||
generateCommandHandler(components[componentName], componentName),
|
||||
]
|
||||
.join('\n\n')
|
||||
.trim();
|
||||
})
|
||||
.join('\n\n');
|
||||
})
|
||||
|
||||
+118
@@ -90,6 +90,86 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)scrollTo:(NSInteger)y animated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
RCT_EXTERN inline void CommandNativeComponentHandleCommand(
|
||||
id<CommandNativeComponentViewProtocol> componentView,
|
||||
NSString const *commandName,
|
||||
NSArray const *args)
|
||||
{
|
||||
if ([commandName isEqualToString:@\\"flashScrollIndicators\\"]) {
|
||||
#if RCT_DEBUG
|
||||
if ([args count] != 0) {
|
||||
RCTLogError(@\\"%@ command %@ received %d arguments, expected %d.\\", @\\"CommandNativeComponent\\", commandName, (int)[args count], 0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
[componentView flashScrollIndicators]
|
||||
return;
|
||||
}
|
||||
|
||||
if ([commandName isEqualToString:@\\"hotspotUpdate\\"]) {
|
||||
#if RCT_DEBUG
|
||||
if ([args count] != 2) {
|
||||
RCTLogError(@\\"%@ command %@ received %d arguments, expected %d.\\", @\\"CommandNativeComponent\\", commandName, (int)[args count], 2);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg0 = args[0];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @\\"number\\", @\\"CommandNativeComponent\\", commandName, @\\"1st\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NSInteger x = [(NSNumber *)arg0 intValue];
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg1 = args[1];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @\\"number\\", @\\"CommandNativeComponent\\", commandName, @\\"2nd\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NSInteger y = [(NSNumber *)arg1 intValue];
|
||||
|
||||
[componentView hotspotUpdate:x y:y]
|
||||
return;
|
||||
}
|
||||
|
||||
if ([commandName isEqualToString:@\\"scrollTo\\"]) {
|
||||
#if RCT_DEBUG
|
||||
if ([args count] != 2) {
|
||||
RCTLogError(@\\"%@ command %@ received %d arguments, expected %d.\\", @\\"CommandNativeComponent\\", commandName, (int)[args count], 2);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg0 = args[0];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @\\"number\\", @\\"CommandNativeComponent\\", commandName, @\\"1st\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NSInteger y = [(NSNumber *)arg0 intValue];
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg1 = args[1];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @\\"boolean\\", @\\"CommandNativeComponent\\", commandName, @\\"2nd\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
BOOL animated = [(NSNumber *)arg1 boolValue];
|
||||
|
||||
[componentView scrollTo:y animated:animated]
|
||||
return;
|
||||
}
|
||||
|
||||
#if RCT_DEBUG
|
||||
RCTLogError(@\\"%@ received command %@, which is not a supported command.\\", @\\"CommandNativeComponent\\", commandName);
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END",
|
||||
}
|
||||
`;
|
||||
@@ -113,6 +193,44 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)hotspotUpdate:(NSInteger)x y:(NSInteger)y;
|
||||
@end
|
||||
|
||||
RCT_EXTERN inline void CommandNativeComponentHandleCommand(
|
||||
id<CommandNativeComponentViewProtocol> componentView,
|
||||
NSString const *commandName,
|
||||
NSArray const *args)
|
||||
{
|
||||
if ([commandName isEqualToString:@\\"hotspotUpdate\\"]) {
|
||||
#if RCT_DEBUG
|
||||
if ([args count] != 2) {
|
||||
RCTLogError(@\\"%@ command %@ received %d arguments, expected %d.\\", @\\"CommandNativeComponent\\", commandName, (int)[args count], 2);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg0 = args[0];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @\\"number\\", @\\"CommandNativeComponent\\", commandName, @\\"1st\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NSInteger x = [(NSNumber *)arg0 intValue];
|
||||
|
||||
#if RCT_DEBUG
|
||||
NSObject *arg1 = args[1];
|
||||
if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @\\"number\\", @\\"CommandNativeComponent\\", commandName, @\\"2nd\\")) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
NSInteger y = [(NSNumber *)arg1 intValue];
|
||||
|
||||
[componentView hotspotUpdate:x y:y]
|
||||
return;
|
||||
}
|
||||
|
||||
#if RCT_DEBUG
|
||||
RCTLogError(@\\"%@ received command %@, which is not a supported command.\\", @\\"CommandNativeComponent\\", commandName);
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END",
|
||||
}
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user