Add utility methods for enabling high quality error messages

Summary:
These helper functions will be used by the ObjC generated code for support on commands.

This is an example of what that code might look like and how these functions will be used.
```
- (void)handleCommand:(NSString const *)commandName args:(NSArray const *)args
{
  if ([commandName isEqualToString:@"scrollTo"]) {
    if ([args count] != 2) {
      RCTLogError(
          @"%@ command %@ received %d arguments, expected %d.", @"ScrollView", @"scrollTo", (int)[args count], 2);
      return;
    }

    NSObject *arg0 = args[0];
    if (!RCTValidateTypeOfViewCommandArgument(arg0, [NSNumber class], @"number", @"ScrollView", @"scrollTo", @"1st")) {
      return;
    }

    int x = [(NSNumber *)arg0 intValue];

    NSObject *arg1 = args[1];
    if (!RCTValidateTypeOfViewCommandArgument(arg1, [NSNumber class], @"number", @"ScrollView", @"scrollTo", @"2nd")) {
      return;
    }
    int y = [(NSNumber *)arg1 intValue];

    [self scrollTo:x y:y];
  } else if ([commandName isEqualToString:@"scrollToEnd"]) {
    if ([args count] != 0) {
      RCTLogError(
          @"%@ command %@ received %d arguments, expected %d.", @"ScrollView", @"scrollToEnd", (int)[args count], 0);
      return;
    }

    [self scrollToEnd];
  }
}
```

Reviewed By: JoshuaGross

Differential Revision: D16474117

fbshipit-source-id: 2bb9f01d7c97cc59e9373b7759021c65980fcc0e
This commit is contained in:
Eli White 2019-07-24 19:34:42 -07:00 коммит произвёл Facebook Github Bot
Родитель a24a9b9946
Коммит 91681016e8
3 изменённых файлов: 103 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,64 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
static NSString * RCTLogsError(void (^block)(void))
{
__block NSString* loggedMessage = @"";
__block BOOL loggedError = NO;
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level,
__unused RCTLogSource source,
__unused NSString *fileName,
__unused NSNumber *lineNumber,
NSString *message) {
loggedError = (level == RCTLogLevelError);
loggedMessage = message;
});
if (loggedError) {
return loggedMessage;
}
return nil;
}
@interface RCTUtilsTests : XCTestCase
@end
@implementation RCTUtilsTests
- (void)testRCTHumanReadableType
{
XCTAssertEqualObjects(RCTHumanReadableType(@"str"), @"string");
XCTAssertEqualObjects(RCTHumanReadableType([NSNumber numberWithInt:4]), @"number");
// If we could detect that this was definitely a boolean and not a number that would be ideal
// That seems difficult in ObjC though
XCTAssertEqualObjects(RCTHumanReadableType(@(YES)), @"boolean or number");
XCTAssertEqualObjects(RCTHumanReadableType(@(NO)), @"boolean or number");
// These ideally would just say number
XCTAssertEqualObjects(RCTHumanReadableType([NSNumber numberWithInt:0]), @"boolean or number");
XCTAssertEqualObjects(RCTHumanReadableType([NSNumber numberWithInt:1]), @"boolean or number");
}
- (void)testRCTValidateTypeOfViewCommandArgument
{
XCTAssertEqualObjects(RCTLogsError(^{
RCTValidateTypeOfViewCommandArgument(@"str", [NSNumber class], @"number", @"ScrollView", @"scrollTo", @"2nd");
}), @"ScrollView command scrollTo received 2nd argument of type string, expected number.");
XCTAssertEqualObjects(RCTLogsError(^{
RCTValidateTypeOfViewCommandArgument(@"str", [NSString class], @"string", @"ScrollView", @"scrollTo", @"1st");
}), nil);
}
@end

Просмотреть файл

@ -147,6 +147,9 @@ RCT_EXTERN NSString *RCTColorToHexString(CGColorRef color);
// Get standard localized string (if it exists)
RCT_EXTERN NSString *RCTUIKitLocalizedString(NSString *string);
// Get a human readable type string from an NSObject. For example NSString becomes string
RCT_EXTERN NSString *RCTHumanReadableType(NSObject *obj);
// URL manipulation
RCT_EXTERN NSString *__nullable RCTGetURLQueryParam(NSURL *__nullable URL, NSString *param);
RCT_EXTERN NSURL *__nullable RCTURLByReplacingQueryParam(NSURL *__nullable URL, NSString *param, NSString *__nullable value);
@ -156,4 +159,6 @@ RCT_EXTERN NSString *RCTDropReactPrefixes(NSString *s);
RCT_EXTERN BOOL RCTUIManagerTypeForTagIsFabric(NSNumber *reactTag);
RCT_EXTERN BOOL RCTValidateTypeOfViewCommandArgument(NSObject *obj, id expectedClass, NSString const * expectedType, NSString const *componentName, NSString const * commandName, NSString const * argPos);
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -894,6 +894,22 @@ NSString *RCTUIKitLocalizedString(NSString *string)
return UIKitBundle ? [UIKitBundle localizedStringForKey:string value:string table:nil] : string;
}
NSString *RCTHumanReadableType(NSObject *obj)
{
if ([obj isKindOfClass:[NSString class]]) {
return @"string";
} else if ([obj isKindOfClass:[NSNumber class]]) {
int intVal = [(NSNumber *)obj intValue];
if(intVal == 0 || intVal == 1) {
return @"boolean or number";
}
return @"number";
} else {
return NSStringFromClass([obj class]);
}
}
NSString *__nullable RCTGetURLQueryParam(NSURL *__nullable URL, NSString *param)
{
RCTAssertParam(param);
@ -965,3 +981,21 @@ RCT_EXTERN BOOL RCTUIManagerTypeForTagIsFabric(NSNumber *reactTag)
// See https://github.com/facebook/react/pull/12587
return [reactTag integerValue] % 2 == 0;
}
RCT_EXTERN BOOL RCTValidateTypeOfViewCommandArgument(NSObject *obj, id expectedClass, NSString const * expectedType, NSString const *componentName, NSString const * commandName, NSString const * argPos)
{
if (![obj isKindOfClass:expectedClass]) {
NSString *kindOfClass = RCTHumanReadableType(obj);
RCTLogError(
@"%@ command %@ received %@ argument of type %@, expected %@.",
componentName,
commandName,
argPos,
kindOfClass,
expectedType);
return false;
}
return true;
}