From 407eb4ce85927296837099e7623b6aa67877f547 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Fri, 31 Jul 2015 06:55:47 -0700 Subject: [PATCH] NSNumber arguments must now be nonnull Summary: The bridge implementation on React Android does not currently support boxed numeric/boolean types (the equivalent of NSNumber arguments on iOS), nor does Java support Objective-C's nil messaging system that transparently casts nil to zero, false, etc for primitive types. To avoid platform incompatibilities, we now treat all primitive arguments as non-nullable rather than silently converting NSNull -> nil -> 0/false. We also now enforce that NSNumber * objects must be explicitly marked as `nonnull` (this restriction may be lifted in future if/when Android supports boxed numbers). Other object types are still assumed to be nullable unless specifically annotated with `nonnull`. --- .../RCTMethodArgumentTests.m | 13 ++++ .../RCTModuleMethodTests.m | 78 ++++++++++++++----- Libraries/Network/RCTNetworking.m | 2 +- Libraries/WebSocket/RCTWebSocketManager.m | 6 +- React/Base/RCTModuleMethod.h | 1 + React/Base/RCTModuleMethod.m | 73 ++++++++++++----- React/Modules/RCTTiming.m | 14 ++-- React/Modules/RCTUIManager.m | 58 ++++++-------- React/Views/RCTNavigatorManager.m | 2 +- React/Views/RCTScrollViewManager.m | 5 +- React/Views/RCTWebViewManager.m | 6 +- 11 files changed, 164 insertions(+), 94 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m index 8c4ce86e8d9..3f53812473e 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTMethodArgumentTests.m @@ -128,4 +128,17 @@ extern void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **argTypes XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"BOOL"); } +- (void)testUnused +{ + NSArray *arguments; + NSString *methodName = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar"; + RCTParseObjCMethodName(&methodName, &arguments); + XCTAssertEqualObjects(methodName, @"foo:bar:"); + XCTAssertEqual(arguments.count, (NSUInteger)2); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[0]).type, @"NSString"); + XCTAssertEqualObjects(((RCTMethodArgument *)arguments[1]).type, @"NSNumber"); + XCTAssertTrue(((RCTMethodArgument *)arguments[0]).unused); + XCTAssertFalse(((RCTMethodArgument *)arguments[1]).unused); +} + @end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m index cd8ec7d2963..9c3949d0ea3 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleMethodTests.m @@ -18,6 +18,18 @@ #import "RCTModuleMethod.h" #import "RCTLog.h" +static BOOL RCTLogsError(void (^block)(void)) +{ + __block BOOL loggedError = NO; + RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level, + __unused NSString *fileName, + __unused NSNumber *lineNumber, + __unused NSString *message) { + loggedError = (level == RCTLogLevelError); + }); + return loggedError; +} + @interface RCTModuleMethodTests : XCTestCase @end @@ -32,31 +44,59 @@ RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName JSMethodName:nil moduleClass:[self class]]; + XCTAssertFalse(RCTLogsError(^{ + [method invokeWithBridge:nil module:self arguments:@[@"Hello World"]]; + })); + XCTAssertTrue(RCTLogsError(^{ + [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; + })); +} + +- (void)doFooWithNumber:(__unused NSNumber *)n { } +- (void)doFooWithDouble:(__unused double)n { } +- (void)doFooWithInteger:(__unused NSInteger)n { } + +- (void)testNumbersNonnull +{ { - __block BOOL loggedError = NO; - RCTPerformBlockWithLogFunction(^{ - [method invokeWithBridge:nil module:self arguments:@[@"Hello World"]]; - }, ^(RCTLogLevel level, - __unused NSString *fileName, - __unused NSNumber *lineNumber, - __unused NSString *message) { - loggedError = (level == RCTLogLevelError); - }); - XCTAssertFalse(loggedError); + // Specifying an NSNumber param without nonnull isn't allowed + XCTAssertTrue(RCTLogsError(^{ + NSString *methodName = @"doFooWithNumber:(NSNumber *)n"; + (void)[[RCTModuleMethod alloc] initWithObjCMethodName:methodName + JSMethodName:nil + moduleClass:[self class]]; + })); } { - __block BOOL loggedError = NO; - RCTPerformBlockWithLogFunction(^{ + NSString *methodName = @"doFooWithNumber:(nonnull NSNumber *)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName + JSMethodName:nil + moduleClass:[self class]]; + XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; - }, ^(RCTLogLevel level, - __unused NSString *fileName, - __unused NSNumber *lineNumber, - __unused NSString *message) { - loggedError = (level == RCTLogLevelError); - }); - XCTAssertTrue(loggedError); + })); + } + + { + NSString *methodName = @"doFooWithDouble:(double)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName + JSMethodName:nil + moduleClass:[self class]]; + XCTAssertTrue(RCTLogsError(^{ + [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; + })); + } + + { + NSString *methodName = @"doFooWithInteger:(NSInteger)n"; + RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName + JSMethodName:nil + moduleClass:[self class]]; + XCTAssertTrue(RCTLogsError(^{ + [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; + })); } } diff --git a/Libraries/Network/RCTNetworking.m b/Libraries/Network/RCTNetworking.m index 0f0911c1c8c..c892363b0d9 100644 --- a/Libraries/Network/RCTNetworking.m +++ b/Libraries/Network/RCTNetworking.m @@ -378,7 +378,7 @@ RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query }]; } -RCT_EXPORT_METHOD(cancelRequest:(NSNumber *)requestID) +RCT_EXPORT_METHOD(cancelRequest:(nonnull NSNumber *)requestID) { [_tasksByRequestID[requestID] cancel]; [_tasksByRequestID removeObjectForKey:requestID]; diff --git a/Libraries/WebSocket/RCTWebSocketManager.m b/Libraries/WebSocket/RCTWebSocketManager.m index e859fd965ba..c842cedc6a4 100644 --- a/Libraries/WebSocket/RCTWebSocketManager.m +++ b/Libraries/WebSocket/RCTWebSocketManager.m @@ -58,7 +58,7 @@ RCT_EXPORT_MODULE() } } -RCT_EXPORT_METHOD(connect:(NSURL *)URL socketID:(NSNumber *)socketID) +RCT_EXPORT_METHOD(connect:(NSURL *)URL socketID:(nonnull NSNumber *)socketID) { RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURL:URL]; webSocket.delegate = self; @@ -67,12 +67,12 @@ RCT_EXPORT_METHOD(connect:(NSURL *)URL socketID:(NSNumber *)socketID) [webSocket open]; } -RCT_EXPORT_METHOD(send:(NSString *)message socketID:(NSNumber *)socketID) +RCT_EXPORT_METHOD(send:(NSString *)message socketID:(nonnull NSNumber *)socketID) { [_sockets[socketID] send:message]; } -RCT_EXPORT_METHOD(close:(NSNumber *)socketID) +RCT_EXPORT_METHOD(close:(nonnull NSNumber *)socketID) { [_sockets[socketID] close]; _sockets[socketID] = nil; diff --git a/React/Base/RCTModuleMethod.h b/React/Base/RCTModuleMethod.h index f99f3ac4916..ddb45905696 100644 --- a/React/Base/RCTModuleMethod.h +++ b/React/Base/RCTModuleMethod.h @@ -26,6 +26,7 @@ typedef NS_ENUM(NSUInteger, RCTNullability) { @property (nonatomic, copy, readonly) NSString *type; @property (nonatomic, readonly) RCTNullability nullability; +@property (nonatomic, readonly) BOOL unused; @end diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index 4a2c6938d87..db4a3acf8fe 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -23,10 +23,12 @@ typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id); - (instancetype)initWithType:(NSString *)type nullability:(RCTNullability)nullability + unused:(BOOL)unused { if ((self = [super init])) { _type = [type copy]; _nullability = nullability; + _unused = unused; } return self; } @@ -61,7 +63,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) NSString *constPattern = @"(?:const)"; NSString *nullablePattern = @"(?:__nullable|nullable|__attribute__\\(\\(nullable\\)\\))"; NSString *nonnullPattern = @"(?:__nonnull|nonnull|__attribute__\\(\\(nonnull\\)\\))"; - NSString *annotationPattern = [NSString stringWithFormat:@"(?:(?:%@|%@|(%@)|(%@))\\s*)", + NSString *annotationPattern = [NSString stringWithFormat:@"(?:(?:(%@)|%@|(%@)|(%@))\\s*)", unusedPattern, constPattern, nullablePattern, nonnullPattern]; NSString *pattern = [NSString stringWithFormat:@"(?<=:)(\\s*\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\))?\\s*\\w+", annotationPattern]; @@ -73,11 +75,14 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) NSRange methodRange = {0, methodName.length}; NSMutableArray *args = [NSMutableArray array]; [typeNameRegex enumerateMatchesInString:methodName options:0 range:methodRange usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { - NSRange typeRange = [result rangeAtIndex:4]; + NSRange typeRange = [result rangeAtIndex:5]; NSString *type = typeRange.length ? [methodName substringWithRange:typeRange] : @"id"; - RCTNullability nullability = [result rangeAtIndex:2].length ? RCTNullable : - [result rangeAtIndex:3].length ? RCTNonnullable : RCTNullabilityUnspecified; - [args addObject:[[RCTMethodArgument alloc] initWithType:type nullability:nullability]]; + BOOL unused = ([result rangeAtIndex:2].length > 0); + RCTNullability nullability = [result rangeAtIndex:3].length ? RCTNullable : + [result rangeAtIndex:4].length ? RCTNonnullable : RCTNullabilityUnspecified; + [args addObject:[[RCTMethodArgument alloc] initWithType:type + nullability:nullability + unused:unused]]; }]; *arguments = [args copy]; @@ -153,18 +158,9 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) ) }; - void (^defaultCase)(const char *) = ^(const char *argumentType) { - static const char *blockType = @encode(typeof(^{})); - if (!strcmp(argumentType, blockType)) { - addBlockArgument(); - } else { - RCT_ARG_BLOCK( id value = json; ) - } - }; - for (NSUInteger i = 2; i < numberOfArguments; i++) { const char *objcType = [_methodSignature getArgumentTypeAtIndex:i]; - + BOOL isNullableType = NO; RCTMethodArgument *argument = arguments[i - 2]; NSString *typeName = argument.type; SEL selector = NSSelectorFromString([typeName stringByAppendingString:@":"]); @@ -173,6 +169,9 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) #define RCT_CONVERT_CASE(_value, _type) \ case _value: { \ + if (RCT_DEBUG && ([@#_type hasSuffix:@"*"] || [@#_type hasSuffix:@"Ref"] || [@#_type isEqualToString:@"id"])) { \ + isNullableType = YES; \ + } \ _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \ break; \ @@ -198,6 +197,7 @@ case _value: { \ case '{': { [argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { + NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector]; void *returnValue = malloc(methodSignature.methodReturnLength); NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; @@ -214,8 +214,15 @@ case _value: { \ break; } - default: - defaultCase(objcType); + default: { + static const char *blockType = @encode(typeof(^{})); + if (!strcmp(objcType, blockType)) { + addBlockArgument(); + } else { + RCTLogError(@"Unsupported argument type '%@' in method %@.", + typeName, [self methodName]); + } + } } } else if ([typeName isEqualToString:@"RCTResponseSenderBlock"]) { addBlockArgument(); @@ -278,17 +285,43 @@ case _value: { \ // Unknown argument type RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert" - " to support this type.", typeName, [self methodName]); + " to support this type.", typeName, [self methodName]); } if (RCT_DEBUG) { + RCTNullability nullability = argument.nullability; + if (!isNullableType) { + if (nullability == RCTNullable) { + RCTLogError(@"Argument %tu (%@) of %@.%@ is marked as nullable, but " + "is not a nullable type.", i - 2, typeName, + RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName); + } + nullability = RCTNonnullable; + } + + /** + * Special case - Numbers are not nullable in Android, so we + * don't support this for now. In future we may allow it. + */ + if ([typeName isEqualToString:@"NSNumber"]) { + BOOL unspecified = (nullability == RCTNullabilityUnspecified); + if (!argument.unused && (nullability == RCTNullable || unspecified)) { + RCTLogError(@"Argument %tu (NSNumber) of %@.%@ %@, but React requires " + "that all NSNumber arguments are explicitly marked as " + "`nonnull` to ensure compatibility with Android.", i - 2, + RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, + unspecified ? @"has unspecified nullability" : @"is marked as nullable"); + } + nullability = RCTNonnullable; + } + if (nullability == RCTNonnullable) { RCTArgumentBlock oldBlock = argumentBlocks[i - 2]; argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { if (json == nil || json == (id)kCFNull) { - RCTLogError(@"Argument %tu of %@.%@ must not be null", index, - RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName); + RCTLogError(@"Argument %tu (%@) of %@.%@ must not be null", index, + typeName, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName); id null = nil; [invocation setArgument:&null atIndex:index + 2]; } else { diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index d6387d1d6ac..29e3b3f92f2 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -166,7 +166,7 @@ RCT_EXPORT_MODULE() * calculating the timer's target time. We calculate this by passing in * Date.now() from JS and then subtracting that from the current time here. */ -RCT_EXPORT_METHOD(createTimer:(NSNumber *)callbackID +RCT_EXPORT_METHOD(createTimer:(nonnull NSNumber *)callbackID duration:(NSTimeInterval)jsDuration jsSchedulingTime:(NSDate *)jsSchedulingTime repeats:(BOOL)repeats) @@ -200,15 +200,11 @@ RCT_EXPORT_METHOD(createTimer:(NSNumber *)callbackID [self startTimers]; } -RCT_EXPORT_METHOD(deleteTimer:(NSNumber *)timerID) +RCT_EXPORT_METHOD(deleteTimer:(nonnull NSNumber *)timerID) { - if (timerID) { - _timers[timerID] = nil; - if (_timers.count == 0) { - [self stopTimers]; - } - } else { - RCTLogWarn(@"Called deleteTimer: with a nil timerID"); + _timers[timerID] = nil; + if (_timers.count == 0) { + [self stopTimers]; } } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index de25ef398b4..d1d64ee36b7 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -597,7 +597,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass) * A method to be called from JS, which takes a container ID and then releases * all subviews for that container upon receipt. */ -RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(NSNumber *)containerID) +RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containerID) { id container = _shadowViewRegistry[containerID]; RCTAssert(container != nil, @"container view (for ID %@) not found", containerID); @@ -653,7 +653,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(NSNumber *)containerID) } } -RCT_EXPORT_METHOD(removeRootView:(NSNumber *)rootReactTag) +RCT_EXPORT_METHOD(removeRootView:(nonnull NSNumber *)rootReactTag) { RCTShadowView *rootShadowView = _shadowViewRegistry[rootReactTag]; RCTAssert(rootShadowView.superview == nil, @"root view cannot have superview (ID %@)", rootReactTag); @@ -669,7 +669,7 @@ RCT_EXPORT_METHOD(removeRootView:(NSNumber *)rootReactTag) }]; } -RCT_EXPORT_METHOD(replaceExistingNonRootView:(NSNumber *)reactTag withView:(NSNumber *)newReactTag) +RCT_EXPORT_METHOD(replaceExistingNonRootView:(nonnull NSNumber *)reactTag withView:(nonnull NSNumber *)newReactTag) { RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; RCTAssert(shadowView != nil, @"shadowView (for ID %@) not found", reactTag); @@ -689,7 +689,7 @@ RCT_EXPORT_METHOD(replaceExistingNonRootView:(NSNumber *)reactTag withView:(NSNu removeAtIndices:removeAtIndices]; } -RCT_EXPORT_METHOD(manageChildren:(NSNumber *)containerReactTag +RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag moveFromIndices:(NSArray *)moveFromIndices moveToIndices:(NSArray *)moveToIndices addChildReactTags:(NSArray *)addChildReactTags @@ -809,7 +809,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView [shadowView updateLayout]; } -RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag +RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag viewName:(NSString *)viewName rootTag:(__unused NSNumber *)rootTag props:(NSDictionary *)props) @@ -876,7 +876,7 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag } // TODO: remove viewName param as it isn't needed -RCT_EXPORT_METHOD(updateView:(NSNumber *)reactTag +RCT_EXPORT_METHOD(updateView:(nonnull NSNumber *)reactTag viewName:(__unused NSString *)_ props:(NSDictionary *)props) { @@ -892,9 +892,8 @@ RCT_EXPORT_METHOD(updateView:(NSNumber *)reactTag }]; } -RCT_EXPORT_METHOD(focus:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag) { - if (!reactTag) return; [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { UIView *newResponder = viewRegistry[reactTag]; [newResponder reactWillMakeFirstResponder]; @@ -903,21 +902,16 @@ RCT_EXPORT_METHOD(focus:(NSNumber *)reactTag) }]; } -RCT_EXPORT_METHOD(blur:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(blur:(nonnull NSNumber *)reactTag) { - if (!reactTag) return; [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ UIView *currentResponder = viewRegistry[reactTag]; [currentResponder resignFirstResponder]; }]; } -RCT_EXPORT_METHOD(findSubviewIn:(NSNumber *)reactTag atPoint:(CGPoint)point callback:(RCTResponseSenderBlock)callback) { - if (!reactTag) { - callback(@[(id)kCFNull]); - return; - } - +RCT_EXPORT_METHOD(findSubviewIn:(nonnull NSNumber *)reactTag atPoint:(CGPoint)point callback:(RCTResponseSenderBlock)callback) +{ [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { UIView *view = viewRegistry[reactTag]; UIView *target = [view hitTest:point withEvent:nil]; @@ -1000,14 +994,9 @@ RCT_EXPORT_METHOD(findSubviewIn:(NSNumber *)reactTag atPoint:(CGPoint)point call }); } -RCT_EXPORT_METHOD(measure:(NSNumber *)reactTag +RCT_EXPORT_METHOD(measure:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { - if (!callback) { - RCTLogError(@"Called measure with no callback"); - return; - } - [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { UIView *view = viewRegistry[reactTag]; if (!view) { @@ -1075,8 +1064,8 @@ static void RCTMeasureLayout(RCTShadowView *view, * anything on the main UI thread. Invokes supplied callback with (x, y, width, * height). */ -RCT_EXPORT_METHOD(measureLayout:(NSNumber *)reactTag - relativeTo:(NSNumber *)ancestorReactTag +RCT_EXPORT_METHOD(measureLayout:(nonnull NSNumber *)reactTag + relativeTo:(nonnull NSNumber *)ancestorReactTag errorCallback:(__unused RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { @@ -1092,7 +1081,7 @@ RCT_EXPORT_METHOD(measureLayout:(NSNumber *)reactTag * anything on the main UI thread. Invokes supplied callback with (x, y, width, * height). */ -RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(NSNumber *)reactTag +RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(nonnull NSNumber *)reactTag errorCallback:(__unused RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { @@ -1108,7 +1097,7 @@ RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(NSNumber *)reactTag * passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts. */ RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect - parentView:(NSNumber *)reactTag + parentView:(nonnull NSNumber *)reactTag errorCallback:(__unused RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { @@ -1120,7 +1109,6 @@ RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect NSArray *childShadowViews = [shadowView reactSubviews]; NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:[childShadowViews count]]; - [childShadowViews enumerateObjectsUsingBlock: ^(RCTShadowView *childShadowView, NSUInteger idx, __unused BOOL *stop) { CGRect childLayout = [childShadowView measureLayoutRelativeToAncestor:shadowView]; @@ -1153,9 +1141,9 @@ RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect callback(@[results]); } -RCT_EXPORT_METHOD(setMainScrollViewTag:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(setMainScrollViewTag:(nonnull NSNumber *)reactTag) { - [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ + [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { // - There should be at most one designated "main scroll view" // - There should be at most one designated "`nativeMainScrollDelegate`" // - The one designated main scroll view should have the one designated @@ -1163,8 +1151,8 @@ RCT_EXPORT_METHOD(setMainScrollViewTag:(NSNumber *)reactTag) if (uiManager.mainScrollView) { uiManager.mainScrollView.nativeMainScrollDelegate = nil; } - if (reactTag) { - id view = viewRegistry[reactTag]; + id view = viewRegistry[reactTag]; + if (view) { if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) { uiManager.mainScrollView = (id)view; uiManager.mainScrollView.nativeMainScrollDelegate = uiManager.nativeMainScrollDelegate; @@ -1178,7 +1166,7 @@ RCT_EXPORT_METHOD(setMainScrollViewTag:(NSNumber *)reactTag) } // TODO: we could just pass point property -RCT_EXPORT_METHOD(scrollTo:(NSNumber *)reactTag +RCT_EXPORT_METHOD(scrollTo:(nonnull NSNumber *)reactTag withOffsetX:(CGFloat)offsetX offsetY:(CGFloat)offsetY) { @@ -1193,7 +1181,7 @@ RCT_EXPORT_METHOD(scrollTo:(NSNumber *)reactTag } // TODO: we could just pass point property -RCT_EXPORT_METHOD(scrollWithoutAnimationTo:(NSNumber *)reactTag +RCT_EXPORT_METHOD(scrollWithoutAnimationTo:(nonnull NSNumber *)reactTag offsetX:(CGFloat)offsetX offsetY:(CGFloat)offsetY) { @@ -1207,7 +1195,7 @@ RCT_EXPORT_METHOD(scrollWithoutAnimationTo:(NSNumber *)reactTag }]; } -RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag +RCT_EXPORT_METHOD(zoomToRect:(nonnull NSNumber *)reactTag withRect:(CGRect)rect) { [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ @@ -1224,7 +1212,7 @@ RCT_EXPORT_METHOD(zoomToRect:(NSNumber *)reactTag * JS sets what *it* considers to be the responder. Later, scroll views can use * this in order to determine if scrolling is appropriate. */ -RCT_EXPORT_METHOD(setJSResponder:(NSNumber *)reactTag +RCT_EXPORT_METHOD(setJSResponder:(nonnull NSNumber *)reactTag blockNativeResponder:(__unused BOOL)blockNativeResponder) { [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { diff --git a/React/Views/RCTNavigatorManager.m b/React/Views/RCTNavigatorManager.m index 7d9a780458e..6c76bdbffd8 100644 --- a/React/Views/RCTNavigatorManager.m +++ b/React/Views/RCTNavigatorManager.m @@ -36,7 +36,7 @@ RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger) } // TODO: remove error callbacks -RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag +RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(nonnull NSNumber *)reactTag errorCallback:(__unused RCTResponseSenderBlock)errorCallback callback:(RCTResponseSenderBlock)callback) { diff --git a/React/Views/RCTScrollViewManager.m b/React/Views/RCTScrollViewManager.m index a83bf61dae1..b92e0d9d65d 100644 --- a/React/Views/RCTScrollViewManager.m +++ b/React/Views/RCTScrollViewManager.m @@ -76,7 +76,7 @@ RCT_DEPRECATED_VIEW_PROPERTY(throttleScrollCallbackMS, scrollEventThrottle) }; } -RCT_EXPORT_METHOD(getContentSize:(NSNumber *)reactTag +RCT_EXPORT_METHOD(getContentSize:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { @@ -95,7 +95,7 @@ RCT_EXPORT_METHOD(getContentSize:(NSNumber *)reactTag }]; } -RCT_EXPORT_METHOD(calculateChildFrames:(NSNumber *)reactTag +RCT_EXPORT_METHOD(calculateChildFrames:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { @@ -107,7 +107,6 @@ RCT_EXPORT_METHOD(calculateChildFrames:(NSNumber *)reactTag } NSArray *childFrames = [((RCTScrollView *)view) calculateChildFramesData]; - if (childFrames) { callback(@[childFrames]); } diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m index fd69f667ece..24b5c89dd68 100644 --- a/React/Views/RCTWebViewManager.m +++ b/React/Views/RCTWebViewManager.m @@ -47,7 +47,7 @@ RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); }; } -RCT_EXPORT_METHOD(goBack:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { RCTWebView *view = viewRegistry[reactTag]; @@ -58,7 +58,7 @@ RCT_EXPORT_METHOD(goBack:(NSNumber *)reactTag) }]; } -RCT_EXPORT_METHOD(goForward:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { id view = viewRegistry[reactTag]; @@ -70,7 +70,7 @@ RCT_EXPORT_METHOD(goForward:(NSNumber *)reactTag) } -RCT_EXPORT_METHOD(reload:(NSNumber *)reactTag) +RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { RCTWebView *view = viewRegistry[reactTag];