diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m index a8ad81555c540b..cee52d0368728e 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTSparseArrayTests.m @@ -25,10 +25,10 @@ @implementation RCTSparseArrayTests - (void)testDictionary { - NSObject *myView = [[UIView alloc] init]; + id myView = [[UIView alloc] init]; myView.reactTag = @4; - NSObject *myOtherView = [[UIView alloc] init]; + id myOtherView = [[UIView alloc] init]; myOtherView.reactTag = @5; RCTSparseArray *registry = [[RCTSparseArray alloc] init]; diff --git a/Libraries/ART/RCTConvert+ART.m b/Libraries/ART/RCTConvert+ART.m index e6bc092863d503..4a3903188d9599 100644 --- a/Libraries/ART/RCTConvert+ART.m +++ b/Libraries/ART/RCTConvert+ART.m @@ -144,7 +144,7 @@ + (ARTCGFloatArray)ARTCGFloatArray:(id)json + (ARTBrush *)ARTBrush:(id)json { NSArray *arr = [self NSArray:json]; - NSUInteger type = [self NSUInteger:arr[0]]; + NSUInteger type = [self NSUInteger:arr.firstObject]; switch (type) { case 0: // solid color // These are probably expensive allocations since it's often the same value. diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 80017eaf44a855..dce1c1f5fc381d 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -54,13 +54,6 @@ RCT_EXTERN NSString *const RCTDidCreateNativeModules; */ typedef NSArray *(^RCTBridgeModuleProviderBlock)(void); -/** - * Register the given class as a bridge module. All modules must be registered - * prior to the first bridge initialization. - * - */ -RCT_EXTERN void RCTRegisterModule(Class); - /** * This function returns the module name for a given class. */ @@ -71,7 +64,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); */ @interface RCTBridge : NSObject - /** * Creates a new bridge with a custom RCTBridgeDelegate. * diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index f9eebea1bc790d..c126e8565c0e2e 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -48,6 +48,11 @@ @interface RCTBridge () return RCTModuleClasses; } +/** + * Register the given class as a bridge module. All modules must be registered + * prior to the first bridge initialization. + */ +void RCTRegisterModule(Class); void RCTRegisterModule(Class moduleClass) { static dispatch_once_t onceToken; @@ -57,7 +62,7 @@ void RCTRegisterModule(Class moduleClass) RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)], @"%@ does not conform to the RCTBridgeModule protocol", - NSStringFromClass(moduleClass)); + moduleClass); // Register module [RCTModuleClasses addObject:moduleClass]; diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index 8efbef4629b62b..4d9c4c1fa96838 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -94,7 +94,7 @@ extern dispatch_queue_t RCTJSThread; #define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Class); \ + (NSString *)moduleName { return @#js_name; } \ - + (void)load { RCTRegisterModule([self class]); } + + (void)load { RCTRegisterModule(self); } /** * Wrap the parameter line of your method implementation with this macro to diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 6f23d1d7ab31fc..ec8b8bb341f55a 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -136,21 +136,6 @@ typedef BOOL css_clip_t, css_backface_visibility_t; @end -/** - * This function will attempt to set a property using a json value by first - * inferring the correct type from all available information, and then - * applying an appropriate conversion method. If the property does not - * exist, or the type cannot be inferred, the function will return NO. - */ -RCT_EXTERN BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json); - -/** - * This function attempts to copy a property from the source object to the - * destination object using KVC. If the property does not exist, or cannot - * be set, it will do nothing and return NO. - */ -RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath); - /** * Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these. */ diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 8e6b7476c7eb57..0e7e8232638036 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -1056,177 +1056,3 @@ + (NSPropertyList)NSPropertyList:(id)json }), RCTAnimationTypeEaseInEaseOut, integerValue) @end - -BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json) -{ - // Split keypath - NSArray *parts = [keyPath componentsSeparatedByString:@"."]; - NSString *key = [parts lastObject]; - for (NSUInteger i = 0; i < parts.count - 1; i++) { - target = [target valueForKey:parts[i]]; - if (!target) { - return NO; - } - } - - // Get property setter - SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", - [[key substringToIndex:1] uppercaseString], - [key substringFromIndex:1]]); - - // Fail early - if (![target respondsToSelector:setter]) { - return NO; - } - - @try { - - NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type]; - switch (signature.methodReturnType[0]) { - -#define RCT_SET_CASE(_value, _type) \ - case _value: { \ - _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ - void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ - set(target, setter, convert([RCTConvert class], type, json)); \ - break; \ - } - - RCT_SET_CASE(':', SEL) - RCT_SET_CASE('*', const char *) - RCT_SET_CASE('c', char) - RCT_SET_CASE('C', unsigned char) - RCT_SET_CASE('s', short) - RCT_SET_CASE('S', unsigned short) - RCT_SET_CASE('i', int) - RCT_SET_CASE('I', unsigned int) - RCT_SET_CASE('l', long) - RCT_SET_CASE('L', unsigned long) - RCT_SET_CASE('q', long long) - RCT_SET_CASE('Q', unsigned long long) - RCT_SET_CASE('f', float) - RCT_SET_CASE('d', double) - RCT_SET_CASE('B', BOOL) - RCT_SET_CASE('^', void *) - - case '@': { - id (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; - void (*set)(id, SEL, id) = (typeof(set))objc_msgSend; - set(target, setter, convert([RCTConvert class], type, json)); - break; - } - case '{': - default: { - - // Get converted value - void *value = malloc(signature.methodReturnLength); - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - [invocation setTarget:[RCTConvert class]]; - [invocation setSelector:type]; - [invocation setArgument:&json atIndex:2]; - [invocation invoke]; - [invocation getReturnValue:value]; - - // Set converted value - signature = [target methodSignatureForSelector:setter]; - invocation = [NSInvocation invocationWithMethodSignature:signature]; - [invocation setArgument:&setter atIndex:1]; - [invocation setArgument:value atIndex:2]; - [invocation invokeWithTarget:target]; - free(value); - - break; - } - } - return YES; - } - @catch (NSException *exception) { - RCTLogError(@"Exception thrown while attempting to set property '%@' of \ - '%@' with value '%@': %@", key, [target class], json, exception); - return NO; - } -} - -BOOL RCTCopyProperty(id target, id source, NSString *keyPath) -{ - // Split keypath - NSArray *parts = [keyPath componentsSeparatedByString:@"."]; - NSString *key = [parts lastObject]; - for (NSUInteger i = 0; i < parts.count - 1; i++) { - source = [source valueForKey:parts[i]]; - target = [target valueForKey:parts[i]]; - if (!source || !target) { - return NO; - } - } - - // Get property getter - SEL getter = NSSelectorFromString(key); - - // Get property setter - SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", - [[key substringToIndex:1] uppercaseString], - [key substringFromIndex:1]]); - - // Fail early - if (![source respondsToSelector:getter] || ![target respondsToSelector:setter]) { - return NO; - } - - NSMethodSignature *signature = [source methodSignatureForSelector:getter]; - switch (signature.methodReturnType[0]) { - -#define RCT_COPY_CASE(_value, _type) \ - case _value: { \ - _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ - void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ - set(target, setter, get(source, getter)); \ - break; \ - } - - RCT_COPY_CASE(':', SEL) - RCT_COPY_CASE('*', const char *) - RCT_COPY_CASE('c', char) - RCT_COPY_CASE('C', unsigned char) - RCT_COPY_CASE('s', short) - RCT_COPY_CASE('S', unsigned short) - RCT_COPY_CASE('i', int) - RCT_COPY_CASE('I', unsigned int) - RCT_COPY_CASE('l', long) - RCT_COPY_CASE('L', unsigned long) - RCT_COPY_CASE('q', long long) - RCT_COPY_CASE('Q', unsigned long long) - RCT_COPY_CASE('f', float) - RCT_COPY_CASE('d', double) - RCT_COPY_CASE('B', BOOL) - RCT_COPY_CASE('^', void *) - - case '@': { - id (*get)(id, SEL) = (typeof(get))objc_msgSend; - void (*set)(id, SEL, id) = (typeof(set))objc_msgSend; - set(target, setter, get(source, getter)); - break; - } - case '{': - default: { - - // Get value - void *value = malloc(signature.methodReturnLength); - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - [invocation setArgument:&getter atIndex:1]; - [invocation invokeWithTarget:source]; - [invocation getReturnValue:value]; - - // Set value - signature = [target methodSignatureForSelector:setter]; - invocation = [NSInvocation invocationWithMethodSignature:signature]; - [invocation setArgument:&setter atIndex:1]; - [invocation setArgument:value atIndex:2]; - [invocation invokeWithTarget:target]; - free(value); - - break; - } - } - return YES; -} diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index 47b1de8d2f51e9..c876f4c0acf03b 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -17,7 +17,7 @@ #import "RCTLog.h" #import "RCTUtils.h" -typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id); +typedef void (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id); @implementation RCTMethodArgument @@ -47,7 +47,7 @@ @implementation RCTModuleMethod { Class _moduleClass; SEL _selector; - NSMethodSignature *_methodSignature; + NSInvocation *_invocation; NSArray *_argumentBlocks; } @@ -135,16 +135,20 @@ - (instancetype)initWithObjCMethodName:(NSString *)objCMethodName methodName; }); - // Get method signature - _methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector]; - RCTAssert(_methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName); + // Create method invocation + NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector]; + RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName); + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + [invocation setSelector:_selector]; + [invocation retainArguments]; + _invocation = invocation; // Process arguments - NSUInteger numberOfArguments = _methodSignature.numberOfArguments; + NSUInteger numberOfArguments = methodSignature.numberOfArguments; NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2]; #define RCT_ARG_BLOCK(_logic) \ - [argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \ + [argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \ _logic \ [invocation setArgument:&value atIndex:(index) + 2]; \ }]; @@ -168,7 +172,7 @@ - (instancetype)initWithObjCMethodName:(NSString *)objCMethodName }; for (NSUInteger i = 2; i < numberOfArguments; i++) { - const char *objcType = [_methodSignature getArgumentTypeAtIndex:i]; + const char *objcType = [methodSignature getArgumentTypeAtIndex:i]; BOOL isNullableType = NO; RCTMethodArgument *argument = arguments[i - 2]; NSString *typeName = argument.type; @@ -176,45 +180,54 @@ - (instancetype)initWithObjCMethodName:(NSString *)objCMethodName if ([RCTConvert respondsToSelector:selector]) { switch (objcType[0]) { -#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; \ -} +#define RCT_CASE(_value, _type) \ + case _value: { \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ + RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \ + break; \ + } + + RCT_CASE(_C_CHR, char) + RCT_CASE(_C_UCHR, unsigned char) + RCT_CASE(_C_SHT, short) + RCT_CASE(_C_USHT, unsigned short) + RCT_CASE(_C_INT, int) + RCT_CASE(_C_UINT, unsigned int) + RCT_CASE(_C_LNG, long) + RCT_CASE(_C_ULNG, unsigned long) + RCT_CASE(_C_LNG_LNG, long long) + RCT_CASE(_C_ULNG_LNG, unsigned long long) + RCT_CASE(_C_FLT, float) + RCT_CASE(_C_DBL, double) + RCT_CASE(_C_BOOL, BOOL) + +#define RCT_NULLABLE_CASE(_value, _type) \ + case _value: { \ + isNullableType = YES; \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ + RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \ + break; \ + } + + RCT_NULLABLE_CASE(_C_SEL, SEL) + RCT_NULLABLE_CASE(_C_CHARPTR, const char *) + RCT_NULLABLE_CASE(_C_PTR, void *) + RCT_NULLABLE_CASE(_C_ID, id) - RCT_CONVERT_CASE(':', SEL) - RCT_CONVERT_CASE('*', const char *) - RCT_CONVERT_CASE('c', char) - RCT_CONVERT_CASE('C', unsigned char) - RCT_CONVERT_CASE('s', short) - RCT_CONVERT_CASE('S', unsigned short) - RCT_CONVERT_CASE('i', int) - RCT_CONVERT_CASE('I', unsigned int) - RCT_CONVERT_CASE('l', long) - RCT_CONVERT_CASE('L', unsigned long) - RCT_CONVERT_CASE('q', long long) - RCT_CONVERT_CASE('Q', unsigned long long) - RCT_CONVERT_CASE('f', float) - RCT_CONVERT_CASE('d', double) - RCT_CONVERT_CASE('B', BOOL) - RCT_CONVERT_CASE('@', id) - RCT_CONVERT_CASE('^', void *) - - 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]; - [_invocation setTarget:[RCTConvert class]]; - [_invocation setSelector:selector]; - [_invocation setArgument:&json atIndex:2]; - [_invocation invoke]; - [_invocation getReturnValue:returnValue]; + case _C_STRUCT_B: { + + NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector]; + NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; + [typeInvocation setSelector:selector]; + [typeInvocation setTarget:[RCTConvert class]]; + + [argumentBlocks addObject: + ^(__unused RCTBridge *bridge, NSUInteger index, id json) { + + void *returnValue = malloc(typeSignature.methodReturnLength); + [typeInvocation setArgument:&json atIndex:2]; + [typeInvocation invoke]; + [typeInvocation getReturnValue:returnValue]; [invocation setArgument:returnValue atIndex:index + 2]; @@ -323,13 +336,13 @@ - (instancetype)initWithObjCMethodName:(NSString *)objCMethodName if (nullability == RCTNonnullable) { RCTArgumentBlock oldBlock = argumentBlocks[i - 2]; - argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { + argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) { if (json == nil || json == (id)kCFNull) { RCTLogArgumentError(weakSelf, index, typeName, "must not be null"); id null = nil; [invocation setArgument:&null atIndex:index + 2]; } else { - oldBlock(bridge, invocation, index, json); + oldBlock(bridge, index, json); } }; } @@ -370,22 +383,17 @@ - (void)invokeWithBridge:(RCTBridge *)bridge } } - // Create invocation (we can't re-use this as it wouldn't be thread-safe) - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature]; - [invocation setArgument:&_selector atIndex:1]; - [invocation retainArguments]; - // Set arguments NSUInteger index = 0; for (id json in arguments) { id arg = RCTNilIfNull(json); RCTArgumentBlock block = _argumentBlocks[index]; - block(bridge, invocation, index, arg); + block(bridge, index, arg); index++; } // Invoke method - [invocation invokeWithTarget:module]; + [_invocation invokeWithTarget:module]; } - (NSString *)methodName diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index 626c093eb77dc9..0cdd1b78c4b169 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -244,7 +244,7 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex +- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; RCTPerformanceLoggerEnd(RCTPLTTI); diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index d1f9d5f152d8be..beb9e5d5ac1e8f 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -18,7 +18,7 @@ * Posted right before re-render happens. This is a chance for views to invalidate their state so * next render cycle will pick up updated views and layout appropriately. */ -extern NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification; +RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification; @protocol RCTScrollableProtocol; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 6eb13fa0df2da3..a19682a5a73106 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -9,8 +9,6 @@ #import "RCTUIManager.h" -#import - #import #import "Layout.h" @@ -18,6 +16,8 @@ #import "RCTAnimationType.h" #import "RCTAssert.h" #import "RCTBridge.h" +#import "RCTComponent.h" +#import "RCTComponentData.h" #import "RCTConvert.h" #import "RCTDefines.h" #import "RCTEventDispatcher.h" @@ -30,15 +30,12 @@ #import "RCTUtils.h" #import "RCTView.h" #import "RCTViewManager.h" -#import "RCTViewNodeProtocol.h" #import "UIView+React.h" -typedef void (^react_view_node_block_t)(id); - -static void RCTTraverseViewNodes(id view, react_view_node_block_t block) +static void RCTTraverseViewNodes(id view, void (^block)(id)) { if (view.reactTag) block(view); - for (id subview in view.reactSubviews) { + for (id subview in view.reactSubviews) { RCTTraverseViewNodes(subview, block); } } @@ -199,10 +196,7 @@ @implementation RCTUIManager RCTLayoutAnimation *_layoutAnimation; // Main thread only // Keyed by viewName - NSMutableDictionary *_defaultShadowViews; // RCT thread only - NSMutableDictionary *_defaultViews; // Main thread only - NSDictionary *_viewManagers; - NSDictionary *_viewConfigs; + NSDictionary *_componentDataByName; NSMutableSet *_bridgeTransactionListeners; } @@ -216,42 +210,6 @@ @implementation RCTUIManager */ extern NSString *RCTBridgeModuleNameForClass(Class cls); -/** - * This function derives the view name automatically - * from the module name. - */ -static NSString *RCTViewNameForModuleName(NSString *moduleName) -{ - NSString *name = moduleName; - RCTAssert(name.length, @"Invalid moduleName '%@'", moduleName); - if ([name hasSuffix:@"Manager"]) { - name = [name substringToIndex:name.length - @"Manager".length]; - } - return name; -} - -// TODO: only send name once instead of a dictionary of name and type keyed by name -static NSDictionary *RCTViewConfigForModule(Class managerClass) -{ - unsigned int count = 0; - Method *methods = class_copyMethodList(object_getClass(managerClass), &count); - NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithCapacity:count]; - for (unsigned int i = 0; i < count; i++) { - Method method = methods[i]; - NSString *methodName = NSStringFromSelector(method_getName(method)); - if ([methodName hasPrefix:@"getPropConfig"]) { - NSRange nameRange = [methodName rangeOfString:@"_"]; - if (nameRange.length) { - NSString *name = [methodName substringFromIndex:nameRange.location + 1]; - NSString *type = [managerClass valueForKey:methodName]; - props[name] = type; - } - } - } - free(methods); - return props; -} - - (instancetype)init { if ((self = [super init])) { @@ -260,10 +218,6 @@ - (instancetype)init _pendingUIBlocksLock = [[NSLock alloc] init]; - _defaultShadowViews = [[NSMutableDictionary alloc] init]; - _defaultViews = [[NSMutableDictionary alloc] init]; - - _viewManagerRegistry = [[RCTSparseArray alloc] init]; _shadowViewRegistry = [[RCTSparseArray alloc] init]; _viewRegistry = [[RCTSparseArray alloc] init]; @@ -335,19 +289,15 @@ - (void)setBridge:(RCTBridge *)bridge _shadowViewRegistry = [[RCTSparseArray alloc] init]; // Get view managers from bridge - NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init]; - NSMutableDictionary *viewConfigs = [[NSMutableDictionary alloc] init]; - [_bridge.modules enumerateKeysAndObjectsUsingBlock: - ^(NSString *moduleName, RCTViewManager *manager, __unused BOOL *stop) { + NSMutableDictionary *componentDataByName = [[NSMutableDictionary alloc] init]; + for (RCTViewManager *manager in _bridge.modules.allValues) { if ([manager isKindOfClass:[RCTViewManager class]]) { - NSString *viewName = RCTViewNameForModuleName(moduleName); - viewManagers[viewName] = manager; - viewConfigs[viewName] = RCTViewConfigForModule([manager class]); + RCTComponentData *componentData = [[RCTComponentData alloc] initWithManager:manager]; + componentDataByName[componentData.name] = componentData; } - }]; + } - _viewManagers = [viewManagers copy]; - _viewConfigs = [viewConfigs copy]; + _componentDataByName = [componentDataByName copy]; } - (dispatch_queue_t)methodQueue @@ -435,8 +385,8 @@ - (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView */ - (void)_purgeChildren:(NSArray *)children fromRegistry:(RCTSparseArray *)registry { - for (id child in children) { - RCTTraverseViewNodes(registry[child.reactTag], ^(id subview) { + for (id child in children) { + RCTTraverseViewNodes(registry[child.reactTag], ^(id subview) { RCTAssert(![subview isReactRootView], @"Root views should not be unregistered"); if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) { [(id)subview invalidate]; @@ -529,7 +479,7 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo // properties that aren't related to layout. NSMutableArray *updateBlocks = [[NSMutableArray alloc] init]; for (RCTShadowView *shadowView in viewsWithNewFrames) { - RCTViewManager *manager = _viewManagerRegistry[shadowView.reactTag]; + RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager]; RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView]; if (block) [updateBlocks addObject:block]; } @@ -601,7 +551,7 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo /** * TODO(tadeu): Remove it once and for all */ - for (id node in _bridgeTransactionListeners) { + for (id node in _bridgeTransactionListeners) { [node reactBridgeDidFinishTransaction]; } }; @@ -625,7 +575,7 @@ - (void)_amendPendingUIBlocksWithStylePropagationUpdateForRootView:(RCTShadowVie */ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containerID) { - id container = _shadowViewRegistry[containerID]; + id container = _shadowViewRegistry[containerID]; RCTAssert(container != nil, @"container view (for ID %@) not found", containerID); NSUInteger subviewsCount = [container reactSubviews].count; @@ -648,7 +598,7 @@ - (void)_amendPendingUIBlocksWithStylePropagationUpdateForRootView:(RCTShadowVie * * @returns Array of removed items. */ -- (NSArray *)_childrenToRemoveFromContainer:(id)container +- (NSArray *)_childrenToRemoveFromContainer:(id)container atIndices:(NSArray *)atIndices { // If there are no indices to move or the container has no subviews don't bother @@ -672,7 +622,7 @@ - (NSArray *)_childrenToRemoveFromContainer:(id)container return removedChildren; } -- (void)_removeChildren:(NSArray *)children fromContainer:(id)container +- (void)_removeChildren:(NSArray *)children fromContainer:(id)container { for (id removedChild in children) { [container removeReactSubview:removedChild]; @@ -749,7 +699,7 @@ - (void)_manageChildren:(NSNumber *)containerReactTag removeAtIndices:(NSArray *)removeAtIndices registry:(RCTSparseArray *)registry { - id container = registry[containerReactTag]; + id container = registry[containerReactTag]; RCTAssert(moveFromIndices.count == moveToIndices.count, @"moveFromIndices had size %tu, moveToIndices had size %tu", moveFromIndices.count, moveToIndices.count); RCTAssert(addChildReactTags.count == addAtIndices.count, @"there should be at least one React child to add"); @@ -781,87 +731,19 @@ - (void)_manageChildren:(NSNumber *)containerReactTag } } -static BOOL RCTCallPropertySetter(NSString *key, SEL setter, id value, id view, id defaultView, RCTViewManager *manager) -{ - // TODO: cache respondsToSelector tests - if ([manager respondsToSelector:setter]) { - - if (value == (id)kCFNull) { - value = nil; - } - - void (^block)() = ^{ - ((void (*)(id, SEL, id, id, id))objc_msgSend)(manager, setter, value, view, defaultView); - }; - - if (RCT_DEBUG) { - NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([manager class])); - NSString *logPrefix = [NSString stringWithFormat: - @"Error setting property '%@' of %@ with tag #%@: ", - key, viewName, [view reactTag]]; - - RCTPerformBlockWithLogPrefix(block, logPrefix); - } else { - block(); - } - - return YES; - } - return NO; -} - -static void RCTSetViewProps(NSDictionary *props, UIView *view, - UIView *defaultView, RCTViewManager *manager) -{ - [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, __unused BOOL *stop) { - - SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forView:withDefaultView:", key]); - RCTCallPropertySetter(key, setter, obj, view, defaultView, manager); - - }]; -} - -static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView, - RCTShadowView *defaultView, RCTViewManager *manager) -{ - [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, __unused BOOL *stop) { - - SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forShadowView:withDefaultView:", key]); - RCTCallPropertySetter(key, setter, obj, shadowView, defaultView, manager); - - }]; - - // Update layout - [shadowView updateLayout]; -} - RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag viewName:(NSString *)viewName rootTag:(__unused NSNumber *)rootTag props:(NSDictionary *)props) { - RCTViewManager *manager = _viewManagers[viewName]; - if (manager == nil) { - RCTLogWarn(@"No manager class found for view with module name \"%@\"", viewName); - manager = [[RCTViewManager alloc] init]; + RCTComponentData *componentData = _componentDataByName[viewName]; + if (componentData == nil) { + RCTLogError(@"No component found for view with name \"%@\"", viewName); } - // Register manager - _viewManagerRegistry[reactTag] = manager; - - RCTShadowView *shadowView = [manager shadowView]; - if (shadowView) { - - // Generate default view, used for resetting default props - if (!_defaultShadowViews[viewName]) { - _defaultShadowViews[viewName] = [manager shadowView]; - } - - // Set properties - shadowView.viewName = viewName; - shadowView.reactTag = reactTag; - RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], manager); - } + // Register shadow view + RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag]; + [componentData setProps:props forShadowView:shadowView]; _shadowViewRegistry[reactTag] = shadowView; // Shadow view is the source of truth for background color this is a little @@ -870,51 +752,30 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView UIColor *backgroundColor = shadowView.backgroundColor; [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ - RCTAssertMainThread(); - - UIView *view = [manager view]; - if (view) { - - // Generate default view, used for resetting default props - if (!uiManager->_defaultViews[viewName]) { - // Note the default is setup after the props are read for the first time - // ever for this className - this is ok because we only use the default - // for restoring defaults, which never happens on first creation. - uiManager->_defaultViews[viewName] = [manager view]; - } - - // Set properties - view.reactTag = reactTag; - view.backgroundColor = backgroundColor; - if ([view isKindOfClass:[UIView class]]) { - view.multipleTouchEnabled = YES; - view.userInteractionEnabled = YES; // required for touch handling - view.layer.allowsGroupOpacity = YES; // required for touch handling - } - RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], manager); - - if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) { - [uiManager->_bridgeTransactionListeners addObject:view]; - } + id view = [componentData createViewWithTag:reactTag]; + if ([view respondsToSelector:@selector(setBackgroundColor:)]) { + [(UIView *)view setBackgroundColor:backgroundColor]; + } + [componentData setProps:props forView:view]; + if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) { + [uiManager->_bridgeTransactionListeners addObject:view]; } viewRegistry[reactTag] = view; }]; } -// TODO: remove viewName param as it isn't needed RCT_EXPORT_METHOD(updateView:(nonnull NSNumber *)reactTag - viewName:(__unused NSString *)_ + viewName:(NSString *)viewName props:(NSDictionary *)props) { - RCTViewManager *viewManager = _viewManagerRegistry[reactTag]; - NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([viewManager class])); + RCTComponentData *componentData = _componentDataByName[viewName]; RCTShadowView *shadowView = _shadowViewRegistry[reactTag]; - RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], viewManager); + [componentData setProps:props forShadowView:shadowView]; - [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + [self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { UIView *view = viewRegistry[reactTag]; - RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], viewManager); + [componentData setProps:props forView:view]; }]; } @@ -962,8 +823,8 @@ - (void)batchDidComplete RCTProfileBeginEvent(); // Gather blocks to be executed now that all view hierarchy manipulations have // been completed (note that these may still take place before layout has finished) - for (RCTViewManager *manager in _viewManagers.allValues) { - RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry]; + for (RCTComponentData *componentData in _componentDataByName.allValues) { + RCTViewManagerUIBlock uiBlock = [componentData.manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry]; [self addUIBlock:uiBlock]; } @@ -1356,7 +1217,8 @@ - (NSDictionary *)customBubblingEventTypes }, } mutableCopy]; - for (RCTViewManager *manager in _viewManagers.allValues) { + for (RCTComponentData *componentData in _componentDataByName.allValues) { + RCTViewManager *manager = componentData.manager; if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) { NSDictionary *eventTypes = [manager customBubblingEventTypes]; for (NSString *eventName in eventTypes) { @@ -1417,7 +1279,8 @@ - (NSDictionary *)customDirectEventTypes }, } mutableCopy]; - for (RCTViewManager *manager in _viewManagers.allValues) { + for (RCTComponentData *componentData in _componentDataByName.allValues) { + RCTViewManager *manager = componentData.manager; if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) { NSDictionary *eventTypes = [manager customDirectEventTypes]; if (RCT_DEV) { @@ -1453,9 +1316,9 @@ - (NSDictionary *)constantsToExport }, } mutableCopy]; - [_viewManagers enumerateKeysAndObjectsUsingBlock: - ^(NSString *name, RCTViewManager *manager, __unused BOOL *stop) { - + [_componentDataByName enumerateKeysAndObjectsUsingBlock: + ^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) { + RCTViewManager *manager = componentData.manager; NSMutableDictionary *constantsNamespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; @@ -1469,7 +1332,7 @@ - (NSDictionary *)constantsToExport } // Add native props - constantsNamespace[@"NativeProps"] = _viewConfigs[name]; + constantsNamespace[@"NativeProps"] = [componentData viewConfig]; allJSConstants[name] = [constantsNamespace copy]; }]; diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 692e9742b6daa4..8dabd2f80ff9eb 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 1385D0341B665AAE000A309B /* RCTModuleMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 1385D0331B665AAE000A309B /* RCTModuleMap.m */; }; 138D6A141B53CD290074A87E /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A131B53CD290074A87E /* RCTCache.m */; }; 13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; }; + 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AB90C01B6FA36700713B4F /* RCTComponentData.m */; }; 13AF20451AE707F9005F5298 /* RCTSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AF20441AE707F9005F5298 /* RCTSlider.m */; }; 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; }; 13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; }; @@ -139,6 +140,8 @@ 138D6A131B53CD290074A87E /* RCTCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCache.m; sourceTree = ""; }; 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = ""; }; 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = ""; }; + 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponentData.h; sourceTree = ""; }; + 13AB90C01B6FA36700713B4F /* RCTComponentData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTComponentData.m; sourceTree = ""; }; 13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = ""; }; 13AF20431AE707F8005F5298 /* RCTSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSlider.h; sourceTree = ""; }; 13AF20441AE707F9005F5298 /* RCTSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSlider.m; sourceTree = ""; }; @@ -172,7 +175,7 @@ 13C156041AB1A2840079392D /* RCTWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewManager.m; sourceTree = ""; }; 13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = ""; }; 13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = ""; }; - 13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = ""; }; + 13C325281AA63B6A0048765F /* RCTComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponent.h; sourceTree = ""; }; 13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderDrawing.h; sourceTree = ""; }; 13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBorderDrawing.m; sourceTree = ""; }; 13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = ""; }; @@ -325,6 +328,9 @@ 13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */, 13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */, 13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */, + 13C325281AA63B6A0048765F /* RCTComponent.h */, + 13AB90BF1B6FA36700713B4F /* RCTComponentData.h */, + 13AB90C01B6FA36700713B4F /* RCTComponentData.m */, 13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */, 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */, 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */, @@ -389,7 +395,6 @@ 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */, 13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */, 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */, - 13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */, 13C156011AB1A2840079392D /* RCTWebView.h */, 13C156021AB1A2840079392D /* RCTWebView.m */, 13C156031AB1A2840079392D /* RCTWebViewManager.h */, @@ -653,6 +658,7 @@ 83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */, 83A1FE8F1B62643A00BE0E65 /* RCTModalHostViewManager.m in Sources */, 13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */, + 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */, 138D6A141B53CD290074A87E /* RCTCache.m in Sources */, 13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */, ); diff --git a/React/Views/RCTViewNodeProtocol.h b/React/Views/RCTComponent.h similarity index 74% rename from React/Views/RCTViewNodeProtocol.h rename to React/Views/RCTComponent.h index 6cb51c30890fc3..bc73ac8d2910a5 100644 --- a/React/Views/RCTViewNodeProtocol.h +++ b/React/Views/RCTComponent.h @@ -7,19 +7,21 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#import + /** - * Logical node in a tree of application components. Both `ShadowView`s and - * `UIView+React`s conform to this. Allows us to write utilities that - * reason about trees generally. + * Logical node in a tree of application components. Both `ShadowView` and + * `UIView` conforms to this. Allows us to write utilities that reason about + * trees generally. */ -@protocol RCTViewNodeProtocol +@protocol RCTComponent @property (nonatomic, copy) NSNumber *reactTag; -- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex; -- (void)removeReactSubview:(id)subview; +- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex; +- (void)removeReactSubview:(id)subview; - (NSArray *)reactSubviews; -- (id)reactSuperview; +- (id)reactSuperview; - (NSNumber *)reactTagAtPoint:(CGPoint)point; // View/ShadowView is a root view diff --git a/React/Views/RCTComponentData.h b/React/Views/RCTComponentData.h new file mode 100644 index 00000000000000..20a54ad7b3bb23 --- /dev/null +++ b/React/Views/RCTComponentData.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "RCTComponent.h" +#import "RCTDefines.h" + +@class RCTShadowView; +@class RCTViewManager; + +@interface RCTComponentData : NSObject + +@property (nonatomic, copy, readonly) NSString *name; +@property (nonatomic, strong, readonly) RCTViewManager *manager; + +- (instancetype)initWithManager:(RCTViewManager *)manager NS_DESIGNATED_INITIALIZER; + +- (id)createViewWithTag:(NSNumber *)tag; +- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag; +- (void)setProps:(NSDictionary *)props forView:(id)view; +- (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView; + +- (NSDictionary *)viewConfig; + +@end diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m new file mode 100644 index 00000000000000..2f3aa84587a9de --- /dev/null +++ b/React/Views/RCTComponentData.m @@ -0,0 +1,321 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTComponentData.h" + +#import + +#import "RCTBridge.h" +#import "RCTShadowView.h" +#import "RCTViewManager.h" + +typedef void (^RCTPropBlock)(id view, id json); + +@interface RCTComponentProp : NSObject + +@property (nonatomic, copy, readonly) NSString *type; +@property (nonatomic, copy) RCTPropBlock propBlock; + +@end + +@implementation RCTComponentProp + +- (instancetype)initWithType:(NSString *)type +{ + if ((self = [super init])) { + _type = [type copy]; + } + return self; +} + +@end + +@implementation RCTComponentData +{ + id _defaultView; + RCTShadowView *_defaultShadowView; + NSMutableDictionary *_viewPropBlocks; + NSMutableDictionary *_shadowPropBlocks; +} + +- (instancetype)initWithManager:(RCTViewManager *)manager +{ + if ((self = [super init])) { + _manager = manager; + _viewPropBlocks = [[NSMutableDictionary alloc] init]; + _shadowPropBlocks = [[NSMutableDictionary alloc] init]; + + _name = RCTBridgeModuleNameForClass([manager class]); + RCTAssert(_name.length, @"Invalid moduleName '%@'", _name); + if ([_name hasSuffix:@"Manager"]) { + _name = [_name substringToIndex:_name.length - @"Manager".length]; + } + } + return self; +} + +RCT_NOT_IMPLEMENTED(-init) + +- (id)createViewWithTag:(NSNumber *)tag +{ + RCTAssertMainThread(); + + id view = (id)[_manager view]; + view.reactTag = tag; + if ([view isKindOfClass:[UIView class]]) { + ((UIView *)view).multipleTouchEnabled = YES; + ((UIView *)view).userInteractionEnabled = YES; // required for touch handling + ((UIView *)view).layer.allowsGroupOpacity = YES; // required for touch handling + } + return view; +} + +- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag +{ + RCTShadowView *shadowView = [_manager shadowView]; + shadowView.reactTag = tag; + shadowView.viewName = _name; + return shadowView; +} + +- (RCTPropBlock)propBlockForKey:(NSString *)name defaultView:(id)defaultView +{ + BOOL shadowView = [defaultView isKindOfClass:[RCTShadowView class]]; + NSMutableDictionary *propBlocks = shadowView ? _shadowPropBlocks : _viewPropBlocks; + RCTPropBlock propBlock = propBlocks[name]; + if (!propBlock) { + + __weak RCTComponentData *weakSelf = self; + + // Get type + SEL type = NULL; + NSString *keyPath = nil; + SEL selector = NSSelectorFromString([NSString stringWithFormat:@"propConfig%@_%@", shadowView ? @"Shadow" : @"", name]); + Class managerClass = [_manager class]; + if ([managerClass respondsToSelector:selector]) { + NSArray *typeAndKeyPath = ((NSArray *(*)(id, SEL))objc_msgSend)(managerClass, selector); + type = NSSelectorFromString([typeAndKeyPath[0] stringByAppendingString:@":"]); + keyPath = typeAndKeyPath.count > 1 ? typeAndKeyPath[1] : nil; + } else { + propBlock = ^(__unused id view, __unused id json) {}; + propBlocks[name] = propBlock; + return propBlock; + } + + // Check for custom setter + if ([keyPath isEqualToString:@"__custom__"]) { + + // Get custom setter + SEL customSetter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:for%@View:withDefaultView:", name, shadowView ? @"Shadow" : @""]); + + propBlock = ^(id view, id json) { + ((void (*)(id, SEL, id, id, id))objc_msgSend)( + weakSelf.manager, customSetter, json == (id)kCFNull ? nil : json, view, defaultView + ); + }; + + } else { + + // Disect keypath + NSString *key = name; + NSArray *parts = [keyPath componentsSeparatedByString:@"."]; + if (parts) { + key = [parts lastObject]; + parts = [parts subarrayWithRange:(NSRange){0, parts.count - 1}]; + } + + // Get property getter + SEL getter = NSSelectorFromString(key); + + // Get property setter + SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", + [[key substringToIndex:1] uppercaseString], + [key substringFromIndex:1]]); + + // Build setter block + void (^setterBlock)(id target, id source, id json) = nil; + NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; + switch (typeSignature.methodReturnType[0]) { + +#define RCT_CASE(_value, _type) \ + case _value: { \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ + _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ + void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ + setterBlock = ^(id target, id source, id json) { \ + set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \ + }; \ + break; \ + } + + RCT_CASE(_C_SEL, SEL) + RCT_CASE(_C_CHARPTR, const char *) + RCT_CASE(_C_CHR, char) + RCT_CASE(_C_UCHR, unsigned char) + RCT_CASE(_C_SHT, short) + RCT_CASE(_C_USHT, unsigned short) + RCT_CASE(_C_INT, int) + RCT_CASE(_C_UINT, unsigned int) + RCT_CASE(_C_LNG, long) + RCT_CASE(_C_ULNG, unsigned long) + RCT_CASE(_C_LNG_LNG, long long) + RCT_CASE(_C_ULNG_LNG, unsigned long long) + RCT_CASE(_C_FLT, float) + RCT_CASE(_C_DBL, double) + RCT_CASE(_C_BOOL, BOOL) + RCT_CASE(_C_PTR, void *) + RCT_CASE(_C_ID, id) + + case _C_STRUCT_B: + default: { + + NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; + [typeInvocation setSelector:type]; + [typeInvocation setTarget:[RCTConvert class]]; + + __block NSInvocation *sourceInvocation = nil; + __block NSInvocation *targetInvocation = nil; + + setterBlock = ^(id target, id source, id json) { \ + + // Get value + void *value = malloc(typeSignature.methodReturnLength); + if (json) { + [typeInvocation setArgument:&json atIndex:2]; + [typeInvocation invoke]; + [typeInvocation getReturnValue:value]; + } else { + if (!sourceInvocation && source) { + NSMethodSignature *signature = [source methodSignatureForSelector:getter]; + sourceInvocation = [NSInvocation invocationWithMethodSignature:signature]; + [sourceInvocation setSelector:getter]; + } + [sourceInvocation invokeWithTarget:source]; + [sourceInvocation getReturnValue:value]; + } + + // Set value + if (!targetInvocation && target) { + NSMethodSignature *signature = [target methodSignatureForSelector:setter]; + targetInvocation = [NSInvocation invocationWithMethodSignature:signature]; + [targetInvocation setSelector:setter]; + } + [targetInvocation setArgument:value atIndex:2]; + [targetInvocation invokeWithTarget:target]; + free(value); + }; + break; + } + } + + propBlock = ^(__unused id view, __unused id json) { + + // Follow keypath + id target = view; + for (NSString *part in parts) { + target = [target valueForKey:part]; + } + + if (json == (id)kCFNull) { + + // Copy default property + id source = defaultView; + for (NSString *part in parts) { + source = [source valueForKey:part]; + } + setterBlock(target, source, nil); + + } else { + + // Set property with json + setterBlock(target, nil, json); + } + }; + } + + if (RCT_DEBUG) { + + // Provide more useful log feedback if there's an error + RCTPropBlock unwrappedBlock = propBlock; + propBlock = ^(id view, id json) { + NSString *logPrefix = [NSString stringWithFormat: + @"Error setting property '%@' of %@ with tag #%@: ", + name, weakSelf.name, view.reactTag]; + + RCTPerformBlockWithLogPrefix(^{ unwrappedBlock(view, json); }, logPrefix); + }; + } + + propBlocks[name] = [propBlock copy]; + } + return propBlock; +} + +- (void)setProps:(NSDictionary *)props forView:(id)view +{ + if (!view) { + return; + } + + if (!_defaultView) { + _defaultView = [self createViewWithTag:nil]; + } + + [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) { + [self propBlockForKey:key defaultView:_defaultView](view, json); + }]; +} + +- (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView +{ + if (!shadowView) { + return; + } + + if (!_defaultShadowView) { + _defaultShadowView = [self createShadowViewWithTag:nil]; + } + + [props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) { + [self propBlockForKey:key defaultView:_defaultShadowView](shadowView, json); + }]; + + [shadowView updateLayout]; +} + +- (NSDictionary *)viewConfig +{ + Class managerClass = [_manager class]; + NSMutableDictionary *propTypes = [[NSMutableDictionary alloc] init]; + + unsigned int count = 0; + Method *methods = class_copyMethodList(object_getClass(managerClass), &count); + for (unsigned int i = 0; i < count; i++) { + Method method = methods[i]; + SEL selector = method_getName(method); + NSString *methodName = NSStringFromSelector(selector); + if ([methodName hasPrefix:@"propConfig"]) { + NSRange nameRange = [methodName rangeOfString:@"_"]; + if (nameRange.length) { + NSString *name = [methodName substringFromIndex:nameRange.location + 1]; + NSString *type = ((NSArray *(*)(id, SEL))objc_msgSend)(managerClass, selector)[0]; + if (RCT_DEBUG && propTypes[name] && ![propTypes[name] isEqualToString:type]) { + RCTLogError(@"Property '%@' of component '%@' redefined from '%@' " + "to '%@'", name, _name, propTypes[name], type); + } + propTypes[name] = type; + } + } + } + free(methods); + + return propTypes; +} + +@end diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index 23e7d707d3e33b..dd16f9e6c09bbb 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -25,6 +25,7 @@ @implementation RCTModalHostView } RCT_NOT_IMPLEMENTED(-initWithFrame:(CGRect)frame) +RCT_NOT_IMPLEMENTED(-initWithCoder:coder) - (instancetype)initWithBridge:(RCTBridge *)bridge { diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index eaed6963ef6d3a..deb0e63676e911 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -10,7 +10,7 @@ #import #import "Layout.h" -#import "RCTViewNodeProtocol.h" +#import "RCTComponent.h" @class RCTSparseArray; @@ -32,14 +32,14 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); * 3. If a node is "computed" and the constraint passed from above is identical to the constraint used to * perform the last computation, we skip laying out the subtree entirely. */ -@interface RCTShadowView : NSObject +@interface RCTShadowView : NSObject @property (nonatomic, weak, readonly) RCTShadowView *superview; @property (nonatomic, assign, readonly) css_node_t *cssNode; @property (nonatomic, copy) NSString *viewName; @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; -@property (nonatomic, assign) BOOL hasOnLayout; +@property (nonatomic, assign, getter=hasOnLayout) BOOL onLayout; /** * isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is @@ -104,7 +104,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); @property (nonatomic, assign) css_justify_t justifyContent; @property (nonatomic, assign) css_align_t alignSelf; @property (nonatomic, assign) css_align_t alignItems; -@property (nonatomic, assign) css_position_type_t positionType; +@property (nonatomic, assign) css_position_type_t position; @property (nonatomic, assign) css_wrap_type_t flexWrap; @property (nonatomic, assign) CGFloat flex; diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index b83ae01541a2a8..4defde806f1992 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -541,7 +541,7 @@ - (type)getProp \ RCT_STYLE_PROPERTY(JustifyContent, justifyContent, justify_content, css_justify_t) RCT_STYLE_PROPERTY(AlignSelf, alignSelf, align_self, css_align_t) RCT_STYLE_PROPERTY(AlignItems, alignItems, align_items, css_align_t) -RCT_STYLE_PROPERTY(PositionType, positionType, position_type, css_position_type_t) +RCT_STYLE_PROPERTY(Position, position, position_type, css_position_type_t) RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t) - (void)setBackgroundColor:(UIColor *)color diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index ede470292aff7e..3ec37ec07955dd 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -21,11 +21,6 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry); -/** - * Underlying implementation of RCT_EXPORT_XXX macros. Ignore this. - */ -RCT_EXTERN void RCTSetViewProperty(NSString *, NSString *, SEL, id, id, id); - @interface RCTViewManager : NSObject /** @@ -105,35 +100,28 @@ RCT_EXTERN void RCTSetViewProperty(NSString *, NSString *, SEL, id, id, id); /** * This handles the simple case, where JS and native property names match. */ -#define RCT_EXPORT_VIEW_PROPERTY(name, type) RCT_REMAP_VIEW_PROPERTY(name, name, type) - -#define RCT_EXPORT_SHADOW_PROPERTY(name, type) RCT_REMAP_SHADOW_PROPERTY(name, name, type) +#define RCT_EXPORT_VIEW_PROPERTY(name, type) \ ++ (NSArray *)propConfig_##name { return @[@#type]; } /** - * This macro maps a named property on the module to an arbitrary key path - * within the view or shadowView. + * This macro maps a named property to an arbitrary key path in the view. */ -#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \ -RCT_CUSTOM_VIEW_PROPERTY(name, type, UIView) { \ - RCTSetViewProperty(@#name, @#keyPath, @selector(type:), view, defaultView, json); \ -} - -#define RCT_REMAP_SHADOW_PROPERTY(name, keyPath, type) \ -RCT_CUSTOM_SHADOW_PROPERTY(name, type, RCTShadowView) { \ - RCTSetViewProperty(@#name, @#keyPath, @selector(type:), view, defaultView, json); \ -} +#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \ ++ (NSArray *)propConfig_##name { return @[@#type, @#keyPath]; } /** - * These macros can be used when you need to provide custom logic for setting + * This macro can be used when you need to provide custom logic for setting * view properties. The macro should be followed by a method body, which can * refer to "json", "view" and "defaultView" to implement the required logic. */ #define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \ -+ (NSString *)getPropConfigView_##name { return @#type; } \ +RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \ - (void)set_##name:(id)json forView:(viewClass *)view withDefaultView:(viewClass *)defaultView -#define RCT_CUSTOM_SHADOW_PROPERTY(name, type, viewClass) \ -+ (NSString *)getPropConfigShadow_##name { return @#type; } \ -- (void)set_##name:(id)json forShadowView:(viewClass *)view withDefaultView:(viewClass *)defaultView +/** + * This macro is used to map properties to the shadow view, instead of the view. + */ +#define RCT_EXPORT_SHADOW_PROPERTY(name, type) \ ++ (NSArray *)propConfigShadow_##name { return @[@#type]; } @end diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index a892c4d8d60033..51bbb45f205ac0 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -43,15 +43,6 @@ @implementation RCTConvert(UIAccessibilityTraits) @end -void RCTSetViewProperty(NSString *name, NSString *keyPath, SEL type, - id view, id defaultView, id json) -{ - if ((json && !RCTSetProperty(view, keyPath, type, json)) || - (!json && !RCTCopyProperty(view, defaultView, keyPath))) { - RCTLogError(@"%@ does not have setter for `%@` property", [view class], name); - } -} - @implementation RCTViewManager @synthesize bridge = _bridge; @@ -273,8 +264,8 @@ - (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json RCT_EXPORT_SHADOW_PROPERTY(justifyContent, css_justify_t) RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t) -RCT_REMAP_SHADOW_PROPERTY(position, positionType, css_position_type_t) +RCT_EXPORT_SHADOW_PROPERTY(position, css_position_type_t) -RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL) +RCT_EXPORT_SHADOW_PROPERTY(onLayout, BOOL) @end diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index 2775e6ede87767..1c3a30933e6d35 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -9,11 +9,11 @@ #import -#import "RCTViewNodeProtocol.h" +#import "RCTComponent.h" //TODO: let's try to eliminate this category if possible -@interface UIView (React) +@interface UIView (React) /** * Used by the UIIManager to set the view frame.