Break apart -[RCTComponentData propBlockForKey:inDictionary:]
Reviewed By: shergin Differential Revision: D4578549 fbshipit-source-id: 726773fdf8c97cdcc3b0540b32278e36c00b19b5
This commit is contained in:
parent
ebe3355de7
commit
bdd27f4696
|
@ -19,31 +19,13 @@
|
|||
#import "UIView+React.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
|
||||
typedef NSMutableDictionary<NSString *, RCTPropBlock> RCTPropBlockDictionary;
|
||||
|
||||
@implementation RCTComponentData
|
||||
{
|
||||
id<RCTComponent> _defaultView; // Only needed for RCT_CUSTOM_VIEW_PROPERTY
|
||||
NSMutableDictionary<NSString *, RCTPropBlock> *_viewPropBlocks;
|
||||
NSMutableDictionary<NSString *, RCTPropBlock> *_shadowPropBlocks;
|
||||
RCTPropBlockDictionary *_viewPropBlocks;
|
||||
RCTPropBlockDictionary *_shadowPropBlocks;
|
||||
BOOL _implementsUIBlockToAmendWithShadowViewRegistry;
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
@ -120,154 +102,48 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return shadowView;
|
||||
}
|
||||
|
||||
- (RCTPropBlock)propBlockForKey:(NSString *)name
|
||||
inDictionary:(NSMutableDictionary<NSString *, RCTPropBlock> *)propBlocks
|
||||
- (void)callCustomSetter:(SEL)setter onView:(id<RCTComponent>)view withProp:(id)json isShadowView:(BOOL)isShadowView
|
||||
{
|
||||
BOOL shadowView = (propBlocks == _shadowPropBlocks);
|
||||
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]);
|
||||
if ([_managerClass respondsToSelector:selector]) {
|
||||
NSArray<NSString *> *typeAndKeyPath =
|
||||
((NSArray<NSString *> *(*)(id, SEL))objc_msgSend)(_managerClass, selector);
|
||||
type = RCTConvertSelectorForType(typeAndKeyPath[0]);
|
||||
keyPath = typeAndKeyPath.count > 1 ? typeAndKeyPath[1] : nil;
|
||||
} else {
|
||||
propBlock = ^(__unused id view, __unused id json) {};
|
||||
propBlocks[name] = propBlock;
|
||||
return propBlock;
|
||||
json = RCTNilIfNull(json);
|
||||
if (!isShadowView) {
|
||||
if (!json && !_defaultView) {
|
||||
// Only create default view if json is null
|
||||
_defaultView = [self createViewWithTag:nil];
|
||||
}
|
||||
|
||||
// Check for custom setter
|
||||
if ([keyPath isEqualToString:@"__custom__"]) {
|
||||
|
||||
// Get custom setter. There is no default view in the shadow case, so the selector is different.
|
||||
NSString *selectorString;
|
||||
if (!shadowView) {
|
||||
selectorString = [NSString stringWithFormat:@"set_%@:for%@View:withDefaultView:", name, shadowView ? @"Shadow" : @""];
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(self.manager, setter, json, view, _defaultView);
|
||||
} else {
|
||||
selectorString = [NSString stringWithFormat:@"set_%@:forShadowView:", name];
|
||||
((void (*)(id, SEL, id, id))objc_msgSend)(self.manager, setter, json, view);
|
||||
}
|
||||
SEL customSetter = NSSelectorFromString(selectorString);
|
||||
}
|
||||
|
||||
propBlock = ^(id<RCTComponent> view, id json) {
|
||||
RCTComponentData *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
static RCTPropBlock createEventSetter(NSString *propName, SEL setter, RCTBridge *bridge)
|
||||
{
|
||||
__weak RCTBridge *weakBridge = bridge;
|
||||
return ^(id target, id json) {
|
||||
void (^eventHandler)(NSDictionary *event) = nil;
|
||||
if ([RCTConvert BOOL:json]) {
|
||||
__weak id<RCTComponent> weakTarget = target;
|
||||
eventHandler = ^(NSDictionary *event) {
|
||||
// The component no longer exists, we shouldn't send the event
|
||||
id<RCTComponent> strongTarget = weakTarget;
|
||||
if (!strongTarget) {
|
||||
return;
|
||||
}
|
||||
json = RCTNilIfNull(json);
|
||||
if (!shadowView) {
|
||||
if (!json && !strongSelf->_defaultView) {
|
||||
// Only create default view if json is null
|
||||
strongSelf->_defaultView = [strongSelf createViewWithTag:nil];
|
||||
}
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(
|
||||
strongSelf.manager, customSetter, json, view, strongSelf->_defaultView
|
||||
);
|
||||
} else {
|
||||
((void (*)(id, SEL, id, id))objc_msgSend)(
|
||||
strongSelf.manager, customSetter, json, view
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
// Disect keypath
|
||||
NSString *key = name;
|
||||
NSArray<NSString *> *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 json) = nil;
|
||||
if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") ||
|
||||
type == NSSelectorFromString(@"RCTDirectEventBlock:")) {
|
||||
|
||||
// Special case for event handlers
|
||||
__weak RCTViewManager *weakManager = self.manager;
|
||||
setterBlock = ^(id target, id json) {
|
||||
__weak id<RCTComponent> weakTarget = target;
|
||||
((void (*)(id, SEL, id))objc_msgSend)(target, setter, [RCTConvert BOOL:json] ? ^(NSDictionary *body) {
|
||||
body = [NSMutableDictionary dictionaryWithDictionary:body];
|
||||
((NSMutableDictionary *)body)[@"target"] = weakTarget.reactTag;
|
||||
NSMutableDictionary *mutableEvent = [NSMutableDictionary dictionaryWithDictionary:event];
|
||||
mutableEvent[@"target"] = strongTarget.reactTag;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[weakManager.bridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(name) body:body];
|
||||
[weakBridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(propName) body:mutableEvent];
|
||||
#pragma clang diagnostic pop
|
||||
} : nil);
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
// Ordinary property handlers
|
||||
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
|
||||
if (!typeSignature) {
|
||||
RCTLogError(@"No +[RCTConvert %@] function found.", NSStringFromSelector(type));
|
||||
return ^(__unused id<RCTComponent> view, __unused id json){};
|
||||
}
|
||||
switch (typeSignature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
__block BOOL setDefaultValue = NO; \
|
||||
__block _type defaultValue; \
|
||||
_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 json) { \
|
||||
if (json) { \
|
||||
if (!setDefaultValue && target) { \
|
||||
if ([target respondsToSelector:getter]) { \
|
||||
defaultValue = get(target, getter); \
|
||||
} \
|
||||
setDefaultValue = YES; \
|
||||
} \
|
||||
set(target, setter, convert([RCTConvert class], type, json)); \
|
||||
} else if (setDefaultValue) { \
|
||||
set(target, setter, defaultValue); \
|
||||
} \
|
||||
}; \
|
||||
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: {
|
||||
((void (*)(id, SEL, id))objc_msgSend)(target, setter, eventHandler);
|
||||
};
|
||||
}
|
||||
|
||||
static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, SEL type, SEL getter, SEL setter)
|
||||
{
|
||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||
typeInvocation.selector = type;
|
||||
typeInvocation.target = [RCTConvert class];
|
||||
|
@ -275,8 +151,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
__block NSInvocation *targetInvocation = nil;
|
||||
__block NSMutableData *defaultValue = nil;
|
||||
|
||||
setterBlock = ^(id target, id json) { \
|
||||
|
||||
return ^(id target, id json) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
@ -327,13 +202,119 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
free(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
- (RCTPropBlock)createPropBlock:(NSString *)name isShadowView:(BOOL)isShadowView
|
||||
{
|
||||
// Get type
|
||||
SEL type = NULL;
|
||||
NSString *keyPath = nil;
|
||||
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"propConfig%@_%@", isShadowView ? @"Shadow" : @"", name]);
|
||||
if ([_managerClass respondsToSelector:selector]) {
|
||||
NSArray<NSString *> *typeAndKeyPath = ((NSArray<NSString *> *(*)(id, SEL))objc_msgSend)(_managerClass, selector);
|
||||
type = RCTConvertSelectorForType(typeAndKeyPath[0]);
|
||||
keyPath = typeAndKeyPath.count > 1 ? typeAndKeyPath[1] : nil;
|
||||
} else {
|
||||
return ^(__unused id view, __unused id json) {};
|
||||
}
|
||||
|
||||
// Check for custom setter
|
||||
if ([keyPath isEqualToString:@"__custom__"]) {
|
||||
// Get custom setter. There is no default view in the shadow case, so the selector is different.
|
||||
NSString *selectorString;
|
||||
if (!isShadowView) {
|
||||
selectorString = [NSString stringWithFormat:@"set_%@:for%@View:withDefaultView:", name, isShadowView ? @"Shadow" : @""];
|
||||
} else {
|
||||
selectorString = [NSString stringWithFormat:@"set_%@:forShadowView:", name];
|
||||
}
|
||||
|
||||
SEL customSetter = NSSelectorFromString(selectorString);
|
||||
__weak RCTComponentData *weakSelf = self;
|
||||
return ^(id<RCTComponent> view, id json) {
|
||||
[weakSelf callCustomSetter:customSetter onView:view withProp:json isShadowView:isShadowView];
|
||||
};
|
||||
} else {
|
||||
// Disect keypath
|
||||
NSString *key = name;
|
||||
NSArray<NSString *> *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 json) = nil;
|
||||
if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") ||
|
||||
type == NSSelectorFromString(@"RCTDirectEventBlock:")) {
|
||||
// Special case for event handlers
|
||||
setterBlock = createEventSetter(name, setter, _bridge);
|
||||
} else {
|
||||
// Ordinary property handlers
|
||||
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
|
||||
if (!typeSignature) {
|
||||
RCTLogError(@"No +[RCTConvert %@] function found.", NSStringFromSelector(type));
|
||||
return ^(__unused id<RCTComponent> view, __unused id json){};
|
||||
}
|
||||
switch (typeSignature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
__block BOOL setDefaultValue = NO; \
|
||||
__block _type defaultValue; \
|
||||
_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 json) { \
|
||||
if (json) { \
|
||||
if (!setDefaultValue && target) { \
|
||||
if ([target respondsToSelector:getter]) { \
|
||||
defaultValue = get(target, getter); \
|
||||
} \
|
||||
setDefaultValue = YES; \
|
||||
} \
|
||||
set(target, setter, convert([RCTConvert class], type, json)); \
|
||||
} else if (setDefaultValue) { \
|
||||
set(target, setter, defaultValue); \
|
||||
} \
|
||||
}; \
|
||||
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: {
|
||||
setterBlock = createNSInvocationSetter(typeSignature, type, getter, setter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propBlock = ^(__unused id view, __unused id json) {
|
||||
|
||||
return ^(__unused id view, __unused id json) {
|
||||
// Follow keypath
|
||||
id target = view;
|
||||
for (NSString *part in parts) {
|
||||
|
@ -344,20 +325,27 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
setterBlock(target, RCTNilIfNull(json));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
- (RCTPropBlock)propBlockForKey:(NSString *)name isShadowView:(BOOL)isShadowView
|
||||
{
|
||||
RCTPropBlockDictionary *propBlocks = isShadowView ? _shadowPropBlocks : _viewPropBlocks;
|
||||
RCTPropBlock propBlock = propBlocks[name];
|
||||
if (!propBlock) {
|
||||
propBlock = [self createPropBlock:name isShadowView:isShadowView];
|
||||
|
||||
#if RCT_DEBUG
|
||||
// Provide more useful log feedback if there's an error
|
||||
RCTPropBlock unwrappedBlock = propBlock;
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
propBlock = ^(id<RCTComponent> view, id json) {
|
||||
NSString *logPrefix = [NSString stringWithFormat:
|
||||
@"Error setting property '%@' of %@ with tag #%@: ",
|
||||
NSString *logPrefix = [NSString stringWithFormat:@"Error setting property '%@' of %@ with tag #%@: ",
|
||||
name, weakSelf.name, view.reactTag];
|
||||
|
||||
RCTPerformBlockWithLogPrefix(^{ unwrappedBlock(view, json); }, logPrefix);
|
||||
RCTPerformBlockWithLogPrefix(^{
|
||||
unwrappedBlock(view, json);
|
||||
}, logPrefix);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
propBlocks[name] = [propBlock copy];
|
||||
}
|
||||
return propBlock;
|
||||
|
@ -370,7 +358,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
|
||||
[self propBlockForKey:key inDictionary:self->_viewPropBlocks](view, json);
|
||||
[self propBlockForKey:key isShadowView:NO](view, json);
|
||||
}];
|
||||
|
||||
if ([view respondsToSelector:@selector(didSetProps:)]) {
|
||||
|
@ -385,7 +373,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
|
||||
[self propBlockForKey:key inDictionary:self->_shadowPropBlocks](shadowView, json);
|
||||
[self propBlockForKey:key isShadowView:YES](shadowView, json);
|
||||
}];
|
||||
|
||||
if ([shadowView respondsToSelector:@selector(didSetProps:)]) {
|
||||
|
|
Loading…
Reference in New Issue