Refactor RCTUIManager

Summary:
Moved the view creation & property binding logic out of RCTUIManager into a separate RCTComponentData class - this follows the pattern used with the bridge.

I've also updated the property  binding to use pre-allocated blocks for setting the values, which is more efficient than the previous system that re-contructed the selectors each time it was called. This should improve view update performance significantly.
This commit is contained in:
Nick Lockwood 2015-08-06 15:44:15 -07:00
parent aefdf82cdc
commit deba13f698
21 changed files with 512 additions and 492 deletions

View File

@ -25,10 +25,10 @@
- (void)testDictionary
{
NSObject<RCTViewNodeProtocol> *myView = [[UIView alloc] init];
id<RCTComponent> myView = [[UIView alloc] init];
myView.reactTag = @4;
NSObject<RCTViewNodeProtocol> *myOtherView = [[UIView alloc] init];
id<RCTComponent> myOtherView = [[UIView alloc] init];
myOtherView.reactTag = @5;
RCTSparseArray *registry = [[RCTSparseArray alloc] init];

View File

@ -144,7 +144,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
+ (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.

View File

@ -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 <RCTInvalidating>
/**
* Creates a new bridge with a custom RCTBridgeDelegate.
*

View File

@ -48,6 +48,11 @@ NSArray *RCTGetModuleClasses(void)
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];

View File

@ -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

View File

@ -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.
*/

View File

@ -1056,177 +1056,3 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
}), 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;
}

View File

@ -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 @@ typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id);
{
Class _moduleClass;
SEL _selector;
NSMethodSignature *_methodSignature;
NSInvocation *_invocation;
NSArray *_argumentBlocks;
}
@ -135,16 +135,20 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
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 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
};
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 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
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_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 *)
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)
case '{': {
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) {
#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; \
}
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];
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)
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 @@ case _value: { \
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 @@ case _value: { \
}
}
// 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

View File

@ -244,7 +244,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
return self;
}
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
RCTPerformanceLoggerEnd(RCTPLTTI);

View File

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

View File

@ -9,8 +9,6 @@
#import "RCTUIManager.h"
#import <objc/message.h>
#import <AVFoundation/AVFoundation.h>
#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<RCTViewNodeProtocol>);
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block)
static void RCTTraverseViewNodes(id<RCTComponent> view, void (^block)(id<RCTComponent>))
{
if (view.reactTag) block(view);
for (id<RCTViewNodeProtocol> subview in view.reactSubviews) {
for (id<RCTComponent> subview in view.reactSubviews) {
RCTTraverseViewNodes(subview, block);
}
}
@ -199,10 +196,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
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 @@ RCT_EXPORT_MODULE()
*/
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 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
_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 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
_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 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
*/
- (void)_purgeChildren:(NSArray *)children fromRegistry:(RCTSparseArray *)registry
{
for (id<RCTViewNodeProtocol> child in children) {
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
for (id<RCTComponent> child in children) {
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTComponent> subview) {
RCTAssert(![subview isReactRootView], @"Root views should not be unregistered");
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
[(id<RCTInvalidating>)subview invalidate];
@ -529,7 +479,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
// 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 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
/**
* TODO(tadeu): Remove it once and for all
*/
for (id<RCTViewNodeProtocol> node in _bridgeTransactionListeners) {
for (id<RCTComponent> node in _bridgeTransactionListeners) {
[node reactBridgeDidFinishTransaction];
}
};
@ -625,7 +575,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
*/
RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containerID)
{
id<RCTViewNodeProtocol> container = _shadowViewRegistry[containerID];
id<RCTComponent> container = _shadowViewRegistry[containerID];
RCTAssert(container != nil, @"container view (for ID %@) not found", containerID);
NSUInteger subviewsCount = [container reactSubviews].count;
@ -648,7 +598,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
*
* @returns Array of removed items.
*/
- (NSArray *)_childrenToRemoveFromContainer:(id<RCTViewNodeProtocol>)container
- (NSArray *)_childrenToRemoveFromContainer:(id<RCTComponent>)container
atIndices:(NSArray *)atIndices
{
// If there are no indices to move or the container has no subviews don't bother
@ -672,7 +622,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
return removedChildren;
}
- (void)_removeChildren:(NSArray *)children fromContainer:(id<RCTViewNodeProtocol>)container
- (void)_removeChildren:(NSArray *)children fromContainer:(id<RCTComponent>)container
{
for (id removedChild in children) {
[container removeReactSubview:removedChild];
@ -749,7 +699,7 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag
removeAtIndices:(NSArray *)removeAtIndices
registry:(RCTSparseArray *)registry
{
id<RCTViewNodeProtocol> container = registry[containerReactTag];
id<RCTComponent> 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 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull 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 @@ RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
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<RCTComponent> 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 @@ RCT_EXPORT_METHOD(findSubviewIn:(nonnull NSNumber *)reactTag atPoint:(CGPoint)po
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 @@ RCT_EXPORT_METHOD(clearJSResponder)
},
} 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 @@ RCT_EXPORT_METHOD(clearJSResponder)
},
} 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 @@ RCT_EXPORT_METHOD(clearJSResponder)
},
} 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 @@ RCT_EXPORT_METHOD(clearJSResponder)
}
// Add native props
constantsNamespace[@"NativeProps"] = _viewConfigs[name];
constantsNamespace[@"NativeProps"] = [componentData viewConfig];
allJSConstants[name] = [constantsNamespace copy];
}];

View File

@ -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 = "<group>"; };
13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = "<group>"; };
13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTKeyCommands.m; sourceTree = "<group>"; };
13AB90BF1B6FA36700713B4F /* RCTComponentData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponentData.h; sourceTree = "<group>"; };
13AB90C01B6FA36700713B4F /* RCTComponentData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTComponentData.m; sourceTree = "<group>"; };
13AF1F851AE6E777005F5298 /* RCTDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDefines.h; sourceTree = "<group>"; };
13AF20431AE707F8005F5298 /* RCTSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSlider.h; sourceTree = "<group>"; };
13AF20441AE707F9005F5298 /* RCTSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSlider.m; sourceTree = "<group>"; };
@ -172,7 +175,7 @@
13C156041AB1A2840079392D /* RCTWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewManager.m; sourceTree = "<group>"; };
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = "<group>"; };
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
13C325281AA63B6A0048765F /* RCTComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTComponent.h; sourceTree = "<group>"; };
13CC8A801B17642100940AE7 /* RCTBorderDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBorderDrawing.h; sourceTree = "<group>"; };
13CC8A811B17642100940AE7 /* RCTBorderDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBorderDrawing.m; sourceTree = "<group>"; };
13E067481A70F434002CDEE1 /* RCTUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIManager.h; sourceTree = "<group>"; };
@ -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 */,
);

View File

@ -7,19 +7,21 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <CoreGraphics/CoreGraphics.h>
/**
* 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 <NSObject>
@protocol RCTComponent <NSObject>
@property (nonatomic, copy) NSNumber *reactTag;
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex;
- (void)removeReactSubview:(id<RCTViewNodeProtocol>)subview;
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex;
- (void)removeReactSubview:(id<RCTComponent>)subview;
- (NSArray *)reactSubviews;
- (id<RCTViewNodeProtocol>)reactSuperview;
- (id<RCTComponent>)reactSuperview;
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
// View/ShadowView is a root view

View File

@ -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 <Foundation/Foundation.h>
#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<RCTComponent>)createViewWithTag:(NSNumber *)tag;
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag;
- (void)setProps:(NSDictionary *)props forView:(id<RCTComponent>)view;
- (void)setProps:(NSDictionary *)props forShadowView:(RCTShadowView *)shadowView;
- (NSDictionary *)viewConfig;
@end

View File

@ -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 <objc/message.h>
#import "RCTBridge.h"
#import "RCTShadowView.h"
#import "RCTViewManager.h"
typedef void (^RCTPropBlock)(id<RCTComponent> 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<RCTComponent> _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<RCTComponent>)createViewWithTag:(NSNumber *)tag
{
RCTAssertMainThread();
id<RCTComponent> view = (id<RCTComponent>)[_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<RCTComponent> 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<RCTComponent> 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<RCTComponent>)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

View File

@ -25,6 +25,7 @@
}
RCT_NOT_IMPLEMENTED(-initWithFrame:(CGRect)frame)
RCT_NOT_IMPLEMENTED(-initWithCoder:coder)
- (instancetype)initWithBridge:(RCTBridge *)bridge
{

View File

@ -10,7 +10,7 @@
#import <UIKit/UIKit.h>
#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 <RCTViewNodeProtocol>
@interface RCTShadowView : NSObject <RCTComponent>
@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;

View File

@ -541,7 +541,7 @@ RCT_STYLE_PROPERTY(FlexDirection, flexDirection, flex_direction, css_flex_direct
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

View File

@ -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 <RCTBridgeModule>
/**
@ -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

View File

@ -43,15 +43,6 @@ RCT_MULTI_ENUM_CONVERTER(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 @@ RCT_EXPORT_SHADOW_PROPERTY(flexWrap, css_wrap_type_t)
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

View File

@ -9,11 +9,11 @@
#import <UIKit/UIKit.h>
#import "RCTViewNodeProtocol.h"
#import "RCTComponent.h"
//TODO: let's try to eliminate this category if possible
@interface UIView (React) <RCTViewNodeProtocol>
@interface UIView (React) <RCTComponent>
/**
* Used by the UIIManager to set the view frame.