Better date support
This commit is contained in:
parent
961f301d65
commit
57d0a5a628
|
@ -123,10 +123,17 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This macro is used for creating simple converter functions that just call
|
||||||
|
* the specified getter method on the json value.
|
||||||
|
*/
|
||||||
|
#define RCT_CONVERTER(type, name, getter) \
|
||||||
|
RCT_CUSTOM_CONVERTER(type, name, [json getter])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This macro is used for creating converter functions with arbitrary logic.
|
* This macro is used for creating converter functions with arbitrary logic.
|
||||||
*/
|
*/
|
||||||
#define RCT_CONVERTER_CUSTOM(type, name, code) \
|
#define RCT_CUSTOM_CONVERTER(type, name, code) \
|
||||||
+ (type)name:(id)json \
|
+ (type)name:(id)json \
|
||||||
{ \
|
{ \
|
||||||
if (json == [NSNull null]) { \
|
if (json == [NSNull null]) { \
|
||||||
|
@ -143,20 +150,13 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This macro is used for creating simple converter functions that just call
|
|
||||||
* the specified getter method on the json value.
|
|
||||||
*/
|
|
||||||
#define RCT_CONVERTER(type, name, getter) \
|
|
||||||
RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This macro is similar to RCT_CONVERTER, but specifically geared towards
|
* This macro is similar to RCT_CONVERTER, but specifically geared towards
|
||||||
* numeric types. It will handle string input correctly, and provides more
|
* numeric types. It will handle string input correctly, and provides more
|
||||||
* detailed error reporting if a wrong value is passed in.
|
* detailed error reporting if a wrong value is passed in.
|
||||||
*/
|
*/
|
||||||
#define RCT_NUMBER_CONVERTER(type, getter) \
|
#define RCT_NUMBER_CONVERTER(type, getter) \
|
||||||
RCT_CONVERTER_CUSTOM(type, type, [[self NSNumber:json] getter])
|
RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This macro is used for creating converters for enum types.
|
* This macro is used for creating converters for enum types.
|
||||||
|
@ -189,57 +189,6 @@ RCT_CONVERTER_CUSTOM(type, type, [[self NSNumber:json] getter])
|
||||||
return value ? [value getter] : default; \
|
return value ? [value getter] : default; \
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This macro is used for creating converter functions for structs that consist
|
|
||||||
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
|
|
||||||
*/
|
|
||||||
#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \
|
|
||||||
+ (type)type:(id)json \
|
|
||||||
{ \
|
|
||||||
@try { \
|
|
||||||
static NSArray *fields; \
|
|
||||||
static NSUInteger count; \
|
|
||||||
static dispatch_once_t onceToken; \
|
|
||||||
dispatch_once(&onceToken, ^{ \
|
|
||||||
fields = values; \
|
|
||||||
count = [fields count]; \
|
|
||||||
}); \
|
|
||||||
type result; \
|
|
||||||
if ([json isKindOfClass:[NSArray class]]) { \
|
|
||||||
if ([json count] != count) { \
|
|
||||||
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
|
||||||
} else { \
|
|
||||||
for (NSUInteger i = 0; i < count; i++) { \
|
|
||||||
((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} else if ([json isKindOfClass:[NSDictionary class]]) { \
|
|
||||||
NSDictionary *aliases = _aliases; \
|
|
||||||
if (aliases.count) { \
|
|
||||||
json = [json mutableCopy]; \
|
|
||||||
for (NSString *alias in aliases) { \
|
|
||||||
NSString *key = aliases[alias]; \
|
|
||||||
NSNumber *number = json[key]; \
|
|
||||||
if (number) { \
|
|
||||||
((NSMutableDictionary *)json)[key] = number; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
for (NSUInteger i = 0; i < count; i++) { \
|
|
||||||
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
|
|
||||||
} \
|
|
||||||
} else if (json && json != [NSNull null]) { \
|
|
||||||
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
|
||||||
} \
|
|
||||||
return result; \
|
|
||||||
} \
|
|
||||||
@catch (__unused NSException *e) { \
|
|
||||||
RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
|
||||||
type result; \
|
|
||||||
return result; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This macro is used for creating converter functions for typed arrays.
|
* This macro is used for creating converter functions for typed arrays.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,8 +24,8 @@ RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
|
||||||
RCT_NUMBER_CONVERTER(NSInteger, integerValue)
|
RCT_NUMBER_CONVERTER(NSInteger, integerValue)
|
||||||
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
|
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
|
||||||
|
|
||||||
RCT_CONVERTER_CUSTOM(NSArray *, NSArray, [NSArray arrayWithArray:json])
|
RCT_CUSTOM_CONVERTER(NSArray *, NSArray, [NSArray arrayWithArray:json])
|
||||||
RCT_CONVERTER_CUSTOM(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
|
RCT_CUSTOM_CONVERTER(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
|
||||||
RCT_CONVERTER(NSString *, NSString, description)
|
RCT_CONVERTER(NSString *, NSString, description)
|
||||||
|
|
||||||
+ (NSNumber *)NSNumber:(id)json
|
+ (NSNumber *)NSNumber:(id)json
|
||||||
|
@ -33,7 +33,12 @@ RCT_CONVERTER(NSString *, NSString, description)
|
||||||
if ([json isKindOfClass:[NSNumber class]]) {
|
if ([json isKindOfClass:[NSNumber class]]) {
|
||||||
return json;
|
return json;
|
||||||
} else if ([json isKindOfClass:[NSString class]]) {
|
} else if ([json isKindOfClass:[NSString class]]) {
|
||||||
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
|
static NSNumberFormatter *formatter;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
formatter = [[NSNumberFormatter alloc] init];
|
||||||
|
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||||
|
});
|
||||||
NSNumber *number = [formatter numberFromString:json];
|
NSNumber *number = [formatter numberFromString:json];
|
||||||
if (!number) {
|
if (!number) {
|
||||||
RCTLogError(@"JSON String '%@' could not be interpreted as a number", json);
|
RCTLogError(@"JSON String '%@' could not be interpreted as a number", json);
|
||||||
|
@ -74,12 +79,35 @@ RCT_CONVERTER(NSString *, NSString, description)
|
||||||
return [NSURLRequest requestWithURL:[self NSURL:json]];
|
return [NSURLRequest requestWithURL:[self NSURL:json]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSDate *)NSDate:(id)json
|
||||||
|
{
|
||||||
|
if ([json isKindOfClass:[NSNumber class]]) {
|
||||||
|
return [NSDate dateWithTimeIntervalSince1970:[self NSTimeInterval:json]];
|
||||||
|
} else if ([json isKindOfClass:[NSString class]]) {
|
||||||
|
static NSDateFormatter *formatter;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
formatter = [[NSDateFormatter alloc] init];
|
||||||
|
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
|
||||||
|
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||||
|
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
|
||||||
|
});
|
||||||
|
NSDate *date = [formatter dateFromString:json];
|
||||||
|
if (!date) {
|
||||||
|
RCTLogError(@"JSON String '%@' could not be interpreted as a date. Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
} else if (json && json != [NSNull null]) {
|
||||||
|
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a date", json, [json class]);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
// JS Standard for time is milliseconds
|
// JS Standard for time is milliseconds
|
||||||
RCT_CONVERTER_CUSTOM(NSDate *, NSDate, [NSDate dateWithTimeIntervalSince1970:[self double:json] / 1000.0])
|
RCT_CUSTOM_CONVERTER(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
|
||||||
RCT_CONVERTER_CUSTOM(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
|
|
||||||
|
|
||||||
// JS standard for time zones is minutes.
|
// JS standard for time zones is minutes.
|
||||||
RCT_CONVERTER_CUSTOM(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
|
RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
|
||||||
|
|
||||||
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
|
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
|
||||||
@"auto": @(NSTextAlignmentNatural),
|
@"auto": @(NSTextAlignmentNatural),
|
||||||
|
@ -142,7 +170,58 @@ RCT_ENUM_CONVERTER(UIBarStyle, (@{
|
||||||
}), UIBarStyleDefault, integerValue)
|
}), UIBarStyleDefault, integerValue)
|
||||||
|
|
||||||
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
|
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
|
||||||
RCT_CONVERTER_CUSTOM(CGFloat, CGFloat, [self double:json])
|
/**
|
||||||
|
* This macro is used for creating converter functions for structs that consist
|
||||||
|
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
|
||||||
|
*/
|
||||||
|
#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \
|
||||||
|
+ (type)type:(id)json \
|
||||||
|
{ \
|
||||||
|
@try { \
|
||||||
|
static NSArray *fields; \
|
||||||
|
static NSUInteger count; \
|
||||||
|
static dispatch_once_t onceToken; \
|
||||||
|
dispatch_once(&onceToken, ^{ \
|
||||||
|
fields = values; \
|
||||||
|
count = [fields count]; \
|
||||||
|
}); \
|
||||||
|
type result; \
|
||||||
|
if ([json isKindOfClass:[NSArray class]]) { \
|
||||||
|
if ([json count] != count) { \
|
||||||
|
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
||||||
|
} else { \
|
||||||
|
for (NSUInteger i = 0; i < count; i++) { \
|
||||||
|
((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} else if ([json isKindOfClass:[NSDictionary class]]) { \
|
||||||
|
NSDictionary *aliases = _aliases; \
|
||||||
|
if (aliases.count) { \
|
||||||
|
json = [json mutableCopy]; \
|
||||||
|
for (NSString *alias in aliases) { \
|
||||||
|
NSString *key = aliases[alias]; \
|
||||||
|
NSNumber *number = json[key]; \
|
||||||
|
if (number) { \
|
||||||
|
((NSMutableDictionary *)json)[key] = number; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
for (NSUInteger i = 0; i < count; i++) { \
|
||||||
|
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
|
||||||
|
} \
|
||||||
|
} else if (json && json != [NSNull null]) { \
|
||||||
|
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
||||||
|
} \
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
@catch (__unused NSException *e) { \
|
||||||
|
RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
||||||
|
type result; \
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json])
|
||||||
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]), nil)
|
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]), nil)
|
||||||
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
||||||
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
||||||
|
@ -701,6 +780,7 @@ BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json)
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@try {
|
||||||
// Get converted value
|
// Get converted value
|
||||||
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
||||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||||
|
@ -721,6 +801,12 @@ BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json)
|
||||||
|
|
||||||
return YES;
|
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)
|
BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||||
{
|
{
|
||||||
|
@ -748,7 +834,7 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get converted value
|
// Get value
|
||||||
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
||||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||||
[invocation setArgument:&getter atIndex:1];
|
[invocation setArgument:&getter atIndex:1];
|
||||||
|
@ -757,7 +843,7 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||||
void *value = malloc(length);
|
void *value = malloc(length);
|
||||||
[invocation getReturnValue:value];
|
[invocation getReturnValue:value];
|
||||||
|
|
||||||
// Set converted value
|
// Set value
|
||||||
signature = [target methodSignatureForSelector:setter];
|
signature = [target methodSignatureForSelector:setter];
|
||||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||||
[invocation setArgument:&setter atIndex:1];
|
[invocation setArgument:&setter atIndex:1];
|
||||||
|
|
Loading…
Reference in New Issue