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`.
This commit is contained in:
Nick Lockwood
2015-07-31 06:55:47 -07:00
parent 41dd6fe6ea
commit 407eb4ce85
11 changed files with 164 additions and 94 deletions

View File

@@ -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];
}
}

View File

@@ -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<RCTViewNodeProtocol> 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<RCTScrollableProtocol>)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) {