12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #import "RCTConvert.h"
- #import <objc/message.h>
- #import <CoreText/CoreText.h>
- #import "RCTDefines.h"
- #import "RCTImageSource.h"
- #import "RCTParserUtils.h"
- #import "RCTUtils.h"
- @implementation RCTConvert
- RCT_CONVERTER(id, id, self)
- RCT_CONVERTER(BOOL, BOOL, boolValue)
- RCT_NUMBER_CONVERTER(double, doubleValue)
- RCT_NUMBER_CONVERTER(float, floatValue)
- RCT_NUMBER_CONVERTER(int, intValue)
- RCT_NUMBER_CONVERTER(int64_t, longLongValue);
- RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
- RCT_NUMBER_CONVERTER(NSInteger, integerValue)
- RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
- /**
- * This macro is used for creating converter functions for directly
- * representable json values that require no conversion.
- */
- #if RCT_DEBUG
- #define RCT_JSON_CONVERTER(type) \
- +(type *)type : (id)json \
- { \
- if ([json isKindOfClass:[type class]]) { \
- return json; \
- } else if (json) { \
- RCTLogConvertError(json, @ #type); \
- } \
- return nil; \
- }
- #else
- #define RCT_JSON_CONVERTER(type) \
- +(type *)type : (id)json \
- { \
- return json; \
- }
- #endif
- RCT_JSON_CONVERTER(NSArray)
- RCT_JSON_CONVERTER(NSDictionary)
- RCT_JSON_CONVERTER(NSString)
- RCT_JSON_CONVERTER(NSNumber)
- RCT_CUSTOM_CONVERTER(NSSet *, NSSet, [NSSet setWithArray:json])
- RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncoding])
- + (NSIndexSet *)NSIndexSet:(id)json
- {
- json = [self NSNumberArray:json];
- NSMutableIndexSet *indexSet = [NSMutableIndexSet new];
- for (NSNumber *number in json) {
- NSInteger index = number.integerValue;
- if (RCT_DEBUG && index < 0) {
- RCTLogError(@"Invalid index value %lld. Indices must be positive.", (long long)index);
- }
- [indexSet addIndex:index];
- }
- return indexSet;
- }
- + (NSURL *)NSURL:(id)json
- {
- NSString *path = [self NSString:RCTNilIfNull(json)];
- if (!path) {
- return nil;
- }
- @try { // NSURL has a history of crashing with bad input, so let's be safe
- NSURL *URL = [NSURL URLWithString:path];
- if (URL.scheme) { // Was a well-formed absolute URL
- return URL;
- }
- // Check if it has a scheme
- if ([path rangeOfString:@":"].location != NSNotFound) {
- NSMutableCharacterSet *urlAllowedCharacterSet = [NSMutableCharacterSet new];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLUserAllowedCharacterSet]];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPasswordAllowedCharacterSet]];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLHostAllowedCharacterSet]];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPathAllowedCharacterSet]];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];
- [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLFragmentAllowedCharacterSet]];
- path = [path stringByAddingPercentEncodingWithAllowedCharacters:urlAllowedCharacterSet];
- URL = [NSURL URLWithString:path];
- if (URL) {
- return URL;
- }
- }
- // Assume that it's a local path
- path = path.stringByRemovingPercentEncoding;
- if ([path hasPrefix:@"~"]) {
- // Path is inside user directory
- path = path.stringByExpandingTildeInPath;
- } else if (!path.absolutePath) {
- // Assume it's a resource path
- path = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:path];
- }
- if (!(URL = [NSURL fileURLWithPath:path])) {
- RCTLogConvertError(json, @"a valid URL");
- }
- return URL;
- } @catch (__unused NSException *e) {
- RCTLogConvertError(json, @"a valid URL");
- return nil;
- }
- }
- RCT_ENUM_CONVERTER(
- NSURLRequestCachePolicy,
- (@{
- @"default" : @(NSURLRequestUseProtocolCachePolicy),
- @"reload" : @(NSURLRequestReloadIgnoringLocalCacheData),
- @"force-cache" : @(NSURLRequestReturnCacheDataElseLoad),
- @"only-if-cached" : @(NSURLRequestReturnCacheDataDontLoad),
- }),
- NSURLRequestUseProtocolCachePolicy,
- integerValue)
- + (NSURLRequest *)NSURLRequest:(id)json
- {
- if ([json isKindOfClass:[NSString class]]) {
- NSURL *URL = [self NSURL:json];
- return URL ? [NSURLRequest requestWithURL:URL] : nil;
- }
- if ([json isKindOfClass:[NSDictionary class]]) {
- NSString *URLString = json[@"uri"] ?: json[@"url"];
- NSURL *URL;
- NSString *bundleName = json[@"bundle"];
- if (bundleName) {
- URLString = [NSString stringWithFormat:@"%@.bundle/%@", bundleName, URLString];
- }
- URL = [self NSURL:URLString];
- if (!URL) {
- return nil;
- }
- NSData *body = [self NSData:json[@"body"]];
- NSString *method = [self NSString:json[@"method"]].uppercaseString ?: @"GET";
- NSURLRequestCachePolicy cachePolicy = [self NSURLRequestCachePolicy:json[@"cache"]];
- NSDictionary *headers = [self NSDictionary:json[@"headers"]];
- if ([method isEqualToString:@"GET"] && headers == nil && body == nil &&
- cachePolicy == NSURLRequestUseProtocolCachePolicy) {
- return [NSURLRequest requestWithURL:URL];
- }
- if (headers) {
- __block BOOL allHeadersAreStrings = YES;
- [headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id header, BOOL *stop) {
- if (![header isKindOfClass:[NSString class]]) {
- RCTLogError(
- @"Values of HTTP headers passed must be of type string. "
- "Value of header '%@' is not a string.",
- key);
- allHeadersAreStrings = NO;
- *stop = YES;
- }
- }];
- if (!allHeadersAreStrings) {
- // Set headers to nil here to avoid crashing later.
- headers = nil;
- }
- }
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
- request.HTTPBody = body;
- request.HTTPMethod = method;
- request.cachePolicy = cachePolicy;
- request.allHTTPHeaderFields = headers;
- return [request copy];
- }
- if (json) {
- RCTLogConvertError(json, @"a valid URLRequest");
- }
- return nil;
- }
- + (RCTFileURL *)RCTFileURL:(id)json
- {
- NSURL *fileURL = [self NSURL:json];
- if (!fileURL.fileURL) {
- RCTLogError(@"URI must be a local file, '%@' isn't.", fileURL);
- return nil;
- }
- if (![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
- RCTLogError(@"File '%@' could not be found.", fileURL);
- return nil;
- }
- return fileURL;
- }
- + (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 new];
- 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) {
- RCTLogConvertError(json, @"a date");
- }
- return nil;
- }
- + (NSLocale *)NSLocale:(id)json
- {
- if ([json isKindOfClass:[NSString class]]) {
- NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:json];
- if (!locale) {
- RCTLogError(@"JSON String '%@' could not be interpreted as a valid locale. ", json);
- }
- return locale;
- } else if (json) {
- RCTLogConvertError(json, @"a locale");
- }
- return nil;
- }
- // JS Standard for time is milliseconds
- RCT_CUSTOM_CONVERTER(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
- // JS standard for time zones is minutes.
- RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
- NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
- {
- if (!json) {
- return defaultValue;
- }
- if ([json isKindOfClass:[NSNumber class]]) {
- NSArray *allValues = mapping.allValues;
- if ([allValues containsObject:json] || [json isEqual:defaultValue]) {
- return json;
- }
- RCTLogError(@"Invalid %s '%@'. should be one of: %@", typeName, json, allValues);
- return defaultValue;
- }
- if (RCT_DEBUG && ![json isKindOfClass:[NSString class]]) {
- RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@", typeName, [json classForCoder], json);
- }
- id value = mapping[json];
- if (RCT_DEBUG && !value && [json description].length > 0) {
- RCTLogError(
- @"Invalid %s '%@'. should be one of: %@",
- typeName,
- json,
- [[mapping allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]);
- }
- return value ?: defaultValue;
- }
- NSNumber *RCTConvertMultiEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
- {
- if ([json isKindOfClass:[NSArray class]]) {
- if ([json count] == 0) {
- return defaultValue;
- }
- long long result = 0;
- for (id arrayElement in json) {
- NSNumber *value = RCTConvertEnumValue(typeName, mapping, defaultValue, arrayElement);
- result |= value.longLongValue;
- }
- return @(result);
- }
- return RCTConvertEnumValue(typeName, mapping, defaultValue, json);
- }
- RCT_ENUM_CONVERTER(
- NSLineBreakMode,
- (@{
- @"clip" : @(NSLineBreakByClipping),
- @"head" : @(NSLineBreakByTruncatingHead),
- @"tail" : @(NSLineBreakByTruncatingTail),
- @"middle" : @(NSLineBreakByTruncatingMiddle),
- @"wordWrapping" : @(NSLineBreakByWordWrapping),
- }),
- NSLineBreakByTruncatingTail,
- integerValue)
- RCT_ENUM_CONVERTER(
- NSTextAlignment,
- (@{
- @"auto" : @(NSTextAlignmentNatural),
- @"left" : @(NSTextAlignmentLeft),
- @"center" : @(NSTextAlignmentCenter),
- @"right" : @(NSTextAlignmentRight),
- @"justify" : @(NSTextAlignmentJustified),
- }),
- NSTextAlignmentNatural,
- integerValue)
- RCT_ENUM_CONVERTER(
- NSUnderlineStyle,
- (@{
- @"solid" : @(NSUnderlineStyleSingle),
- @"double" : @(NSUnderlineStyleDouble),
- @"dotted" : @(NSUnderlinePatternDot | NSUnderlineStyleSingle),
- @"dashed" : @(NSUnderlinePatternDash | NSUnderlineStyleSingle),
- }),
- NSUnderlineStyleSingle,
- integerValue)
- RCT_ENUM_CONVERTER(
- RCTBorderStyle,
- (@{
- @"solid" : @(RCTBorderStyleSolid),
- @"dotted" : @(RCTBorderStyleDotted),
- @"dashed" : @(RCTBorderStyleDashed),
- }),
- RCTBorderStyleSolid,
- integerValue)
- RCT_ENUM_CONVERTER(
- RCTTextDecorationLineType,
- (@{
- @"none" : @(RCTTextDecorationLineTypeNone),
- @"underline" : @(RCTTextDecorationLineTypeUnderline),
- @"line-through" : @(RCTTextDecorationLineTypeStrikethrough),
- @"underline line-through" : @(RCTTextDecorationLineTypeUnderlineStrikethrough),
- }),
- RCTTextDecorationLineTypeNone,
- integerValue)
- RCT_ENUM_CONVERTER(
- NSWritingDirection,
- (@{
- @"auto" : @(NSWritingDirectionNatural),
- @"ltr" : @(NSWritingDirectionLeftToRight),
- @"rtl" : @(NSWritingDirectionRightToLeft),
- }),
- NSWritingDirectionNatural,
- integerValue)
- RCT_ENUM_CONVERTER(
- UITextAutocapitalizationType,
- (@{
- @"none" : @(UITextAutocapitalizationTypeNone),
- @"words" : @(UITextAutocapitalizationTypeWords),
- @"sentences" : @(UITextAutocapitalizationTypeSentences),
- @"characters" : @(UITextAutocapitalizationTypeAllCharacters)
- }),
- UITextAutocapitalizationTypeSentences,
- integerValue)
- RCT_ENUM_CONVERTER(
- UITextFieldViewMode,
- (@{
- @"never" : @(UITextFieldViewModeNever),
- @"while-editing" : @(UITextFieldViewModeWhileEditing),
- @"unless-editing" : @(UITextFieldViewModeUnlessEditing),
- @"always" : @(UITextFieldViewModeAlways),
- }),
- UITextFieldViewModeNever,
- integerValue)
- + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC
- {
- static NSDictionary<NSString *, NSNumber *> *mapping;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSMutableDictionary<NSString *, NSNumber *> *temporaryMapping = [NSMutableDictionary dictionaryWithDictionary:@{
- @"default" : @(UIKeyboardTypeDefault),
- @"ascii-capable" : @(UIKeyboardTypeASCIICapable),
- @"numbers-and-punctuation" : @(UIKeyboardTypeNumbersAndPunctuation),
- @"url" : @(UIKeyboardTypeURL),
- @"number-pad" : @(UIKeyboardTypeNumberPad),
- @"phone-pad" : @(UIKeyboardTypePhonePad),
- @"name-phone-pad" : @(UIKeyboardTypeNamePhonePad),
- @"email-address" : @(UIKeyboardTypeEmailAddress),
- @"decimal-pad" : @(UIKeyboardTypeDecimalPad),
- @"twitter" : @(UIKeyboardTypeTwitter),
- @"web-search" : @(UIKeyboardTypeWebSearch),
- // Added for Android compatibility
- @"numeric" : @(UIKeyboardTypeDecimalPad),
- }];
- temporaryMapping[@"ascii-capable-number-pad"] = @(UIKeyboardTypeASCIICapableNumberPad);
- mapping = temporaryMapping;
- });
- UIKeyboardType type = RCTConvertEnumValue("UIKeyboardType", mapping, @(UIKeyboardTypeDefault), json).integerValue;
- return type;
- }
- #if !TARGET_OS_TV
- RCT_MULTI_ENUM_CONVERTER(
- UIDataDetectorTypes,
- (@{
- @"phoneNumber" : @(UIDataDetectorTypePhoneNumber),
- @"link" : @(UIDataDetectorTypeLink),
- @"address" : @(UIDataDetectorTypeAddress),
- @"calendarEvent" : @(UIDataDetectorTypeCalendarEvent),
- @"none" : @(UIDataDetectorTypeNone),
- @"all" : @(UIDataDetectorTypeAll),
- }),
- UIDataDetectorTypePhoneNumber,
- unsignedLongLongValue)
- #if WEBKIT_IOS_10_APIS_AVAILABLE
- RCT_MULTI_ENUM_CONVERTER(
- WKDataDetectorTypes,
- (@{
- @"phoneNumber" : @(WKDataDetectorTypePhoneNumber),
- @"link" : @(WKDataDetectorTypeLink),
- @"address" : @(WKDataDetectorTypeAddress),
- @"calendarEvent" : @(WKDataDetectorTypeCalendarEvent),
- @"trackingNumber" : @(WKDataDetectorTypeTrackingNumber),
- @"flightNumber" : @(WKDataDetectorTypeFlightNumber),
- @"lookupSuggestion" : @(WKDataDetectorTypeLookupSuggestion),
- @"none" : @(WKDataDetectorTypeNone),
- @"all" : @(WKDataDetectorTypeAll),
- }),
- WKDataDetectorTypePhoneNumber,
- unsignedLongLongValue)
- #endif // WEBKIT_IOS_10_APIS_AVAILABLE
- #endif // !TARGET_OS_TV
- RCT_ENUM_CONVERTER(
- UIKeyboardAppearance,
- (@{
- @"default" : @(UIKeyboardAppearanceDefault),
- @"light" : @(UIKeyboardAppearanceLight),
- @"dark" : @(UIKeyboardAppearanceDark),
- }),
- UIKeyboardAppearanceDefault,
- integerValue)
- RCT_ENUM_CONVERTER(
- UIReturnKeyType,
- (@{
- @"default" : @(UIReturnKeyDefault),
- @"go" : @(UIReturnKeyGo),
- @"google" : @(UIReturnKeyGoogle),
- @"join" : @(UIReturnKeyJoin),
- @"next" : @(UIReturnKeyNext),
- @"route" : @(UIReturnKeyRoute),
- @"search" : @(UIReturnKeySearch),
- @"send" : @(UIReturnKeySend),
- @"yahoo" : @(UIReturnKeyYahoo),
- @"done" : @(UIReturnKeyDone),
- @"emergency-call" : @(UIReturnKeyEmergencyCall),
- }),
- UIReturnKeyDefault,
- integerValue)
- RCT_ENUM_CONVERTER(
- UIViewContentMode,
- (@{
- @"scale-to-fill" : @(UIViewContentModeScaleToFill),
- @"scale-aspect-fit" : @(UIViewContentModeScaleAspectFit),
- @"scale-aspect-fill" : @(UIViewContentModeScaleAspectFill),
- @"redraw" : @(UIViewContentModeRedraw),
- @"center" : @(UIViewContentModeCenter),
- @"top" : @(UIViewContentModeTop),
- @"bottom" : @(UIViewContentModeBottom),
- @"left" : @(UIViewContentModeLeft),
- @"right" : @(UIViewContentModeRight),
- @"top-left" : @(UIViewContentModeTopLeft),
- @"top-right" : @(UIViewContentModeTopRight),
- @"bottom-left" : @(UIViewContentModeBottomLeft),
- @"bottom-right" : @(UIViewContentModeBottomRight),
- // Cross-platform values
- @"cover" : @(UIViewContentModeScaleAspectFill),
- @"contain" : @(UIViewContentModeScaleAspectFit),
- @"stretch" : @(UIViewContentModeScaleToFill),
- }),
- UIViewContentModeScaleAspectFill,
- integerValue)
- #if !TARGET_OS_TV
- RCT_ENUM_CONVERTER(
- UIBarStyle,
- (@{
- @"default" : @(UIBarStyleDefault),
- @"black" : @(UIBarStyleBlack),
- @"blackOpaque" : @(UIBarStyleBlackOpaque),
- @"blackTranslucent" : @(UIBarStyleBlackTranslucent),
- }),
- UIBarStyleDefault,
- integerValue)
- #endif
- static void convertCGStruct(const char *type, NSArray *fields, CGFloat *result, id json)
- {
- NSUInteger count = fields.count;
- if ([json isKindOfClass:[NSArray class]]) {
- if (RCT_DEBUG && [json count] != count) {
- RCTLogError(
- @"Expected array with count %llu, but count is %llu: %@",
- (unsigned long long)count,
- (unsigned long long)[json count],
- json);
- } else {
- for (NSUInteger i = 0; i < count; i++) {
- result[i] = [RCTConvert CGFloat:RCTNilIfNull(json[i])];
- }
- }
- } else if ([json isKindOfClass:[NSDictionary class]]) {
- for (NSUInteger i = 0; i < count; i++) {
- result[i] = [RCTConvert CGFloat:RCTNilIfNull(json[fields[i]])];
- }
- } else if (json) {
- RCTLogConvertError(json, @(type));
- }
- }
- /**
- * 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) \
- +(type)type : (id)json \
- { \
- static NSArray *fields; \
- static dispatch_once_t onceToken; \
- dispatch_once(&onceToken, ^{ \
- fields = values; \
- }); \
- type result; \
- convertCGStruct(#type, fields, (CGFloat *)&result, json); \
- return result; \
- }
- RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json])
- RCT_CGSTRUCT_CONVERTER(CGPoint, (@[ @"x", @"y" ]))
- RCT_CGSTRUCT_CONVERTER(CGSize, (@[ @"width", @"height" ]))
- RCT_CGSTRUCT_CONVERTER(CGRect, (@[ @"x", @"y", @"width", @"height" ]))
- RCT_CGSTRUCT_CONVERTER(UIEdgeInsets, (@[ @"top", @"left", @"bottom", @"right" ]))
- RCT_ENUM_CONVERTER(
- CGLineJoin,
- (@{
- @"miter" : @(kCGLineJoinMiter),
- @"round" : @(kCGLineJoinRound),
- @"bevel" : @(kCGLineJoinBevel),
- }),
- kCGLineJoinMiter,
- intValue)
- RCT_ENUM_CONVERTER(
- CGLineCap,
- (@{
- @"butt" : @(kCGLineCapButt),
- @"round" : @(kCGLineCapRound),
- @"square" : @(kCGLineCapSquare),
- }),
- kCGLineCapButt,
- intValue)
- RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[ @"a", @"b", @"c", @"d", @"tx", @"ty" ]))
- static NSString *const RCTFallback = @"fallback";
- static NSString *const RCTFallbackARGB = @"fallback-argb";
- static NSString *const RCTSelector = @"selector";
- static NSString *const RCTIndex = @"index";
- /** The following dictionary defines the react-native semantic colors for ios.
- * If the value for a given name is empty then the name itself
- * is used as the UIColor selector.
- * If the RCTSelector key is present then that value is used for a selector instead
- * of the key name.
- * If the given selector is not available on the running OS version then
- * the RCTFallback selector is used instead.
- * If the RCTIndex key is present then object returned from UIColor is an
- * NSArray and the object at index RCTIndex is to be used.
- */
- static NSDictionary<NSString *, NSDictionary *> *RCTSemanticColorsMap()
- {
- static NSDictionary<NSString *, NSDictionary *> *colorMap = nil;
- if (colorMap == nil) {
- NSMutableDictionary<NSString *, NSDictionary *> *map = [@{
- // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors
- // Label Colors
- @"labelColor" : @{
- // iOS 13.0
- RCTFallbackARGB :
- @(0xFF000000) // fallback for iOS<=12: RGBA returned by this semantic color in light mode on iOS 13
- },
- @"secondaryLabelColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x993c3c43)
- },
- @"tertiaryLabelColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x4c3c3c43)
- },
- @"quaternaryLabelColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x2d3c3c43)
- },
- // Fill Colors
- @"systemFillColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x33787880)
- },
- @"secondarySystemFillColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x28787880)
- },
- @"tertiarySystemFillColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x1e767680)
- },
- @"quaternarySystemFillColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x14747480)
- },
- // Text Colors
- @"placeholderTextColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x4c3c3c43)
- },
- // Standard Content Background Colors
- @"systemBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFffffff)
- },
- @"secondarySystemBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFf2f2f7)
- },
- @"tertiarySystemBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFffffff)
- },
- // Grouped Content Background Colors
- @"systemGroupedBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFf2f2f7)
- },
- @"secondarySystemGroupedBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFffffff)
- },
- @"tertiarySystemGroupedBackgroundColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFf2f2f7)
- },
- // Separator Colors
- @"separatorColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0x493c3c43)
- },
- @"opaqueSeparatorColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFc6c6c8)
- },
- // Link Color
- @"linkColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFF007aff)
- },
- // Nonadaptable Colors
- @"darkTextColor" : @{},
- @"lightTextColor" : @{},
- // https://developer.apple.com/documentation/uikit/uicolor/standard_colors
- // Adaptable Colors
- @"systemBlueColor" : @{},
- @"systemBrownColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFa2845e)
- },
- @"systemGreenColor" : @{},
- @"systemIndigoColor" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFF5856d6)
- },
- @"systemOrangeColor" : @{},
- @"systemPinkColor" : @{},
- @"systemPurpleColor" : @{},
- @"systemRedColor" : @{},
- @"systemTealColor" : @{},
- @"systemYellowColor" : @{},
- // Adaptable Gray Colors
- @"systemGrayColor" : @{},
- @"systemGray2Color" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFaeaeb2)
- },
- @"systemGray3Color" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFc7c7cc)
- },
- @"systemGray4Color" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFd1d1d6)
- },
- @"systemGray5Color" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFe5e5ea)
- },
- @"systemGray6Color" : @{
- // iOS 13.0
- RCTFallbackARGB : @(0xFFf2f2f7)
- },
- } mutableCopy];
- // The color names are the Objective-C UIColor selector names,
- // but Swift selector names are valid as well, so make aliases.
- static NSString *const RCTColorSuffix = @"Color";
- NSMutableDictionary<NSString *, NSDictionary *> *aliases = [NSMutableDictionary new];
- for (NSString *objcSelector in map) {
- RCTAssert([objcSelector hasSuffix:RCTColorSuffix], @"A selector in the color map did not end with the suffix Color.");
- NSMutableDictionary *entry = [map[objcSelector] mutableCopy];
- RCTAssert([entry objectForKey:RCTSelector] == nil, @"Entry should not already have an RCTSelector");
- NSString *swiftSelector = [objcSelector substringToIndex:[objcSelector length] - [RCTColorSuffix length]];
- entry[RCTSelector] = objcSelector;
- aliases[swiftSelector] = entry;
- }
- [map addEntriesFromDictionary:aliases];
- #if DEBUG
- [map addEntriesFromDictionary:@{
- // The follow exist for Unit Tests
- @"unitTestFallbackColor" : @{RCTFallback : @"gridColor"},
- @"unitTestFallbackColorIOS" : @{RCTFallback : @"blueColor"},
- @"unitTestFallbackColorEven" : @{
- RCTSelector : @"unitTestFallbackColorEven",
- RCTIndex : @0,
- RCTFallback : @"controlAlternatingRowBackgroundColors"
- },
- @"unitTestFallbackColorOdd" : @{
- RCTSelector : @"unitTestFallbackColorOdd",
- RCTIndex : @1,
- RCTFallback : @"controlAlternatingRowBackgroundColors"
- },
- }];
- #endif
- colorMap = [map copy];
- }
- return colorMap;
- }
- /** Returns a UIColor based on a semantic color name.
- * Returns nil if the semantic color name is invalid.
- */
- static UIColor *RCTColorFromSemanticColorName(NSString *semanticColorName)
- {
- NSDictionary<NSString *, NSDictionary *> *colorMap = RCTSemanticColorsMap();
- UIColor *color = nil;
- NSDictionary<NSString *, id> *colorInfo = colorMap[semanticColorName];
- if (colorInfo) {
- NSString *semanticColorSelector = colorInfo[RCTSelector];
- if (semanticColorSelector == nil) {
- semanticColorSelector = semanticColorName;
- }
- SEL selector = NSSelectorFromString(semanticColorSelector);
- if (![UIColor respondsToSelector:selector]) {
- NSNumber *fallbackRGB = colorInfo[RCTFallbackARGB];
- if (fallbackRGB != nil) {
- RCTAssert([fallbackRGB isKindOfClass:[NSNumber class]], @"fallback ARGB is not a number");
- return [RCTConvert UIColor:fallbackRGB];
- }
- semanticColorSelector = colorInfo[RCTFallback];
- selector = NSSelectorFromString(semanticColorSelector);
- }
- RCTAssert([UIColor respondsToSelector:selector], @"RCTUIColor does not respond to a semantic color selector.");
- Class klass = [UIColor class];
- IMP imp = [klass methodForSelector:selector];
- id (*getSemanticColorObject)(id, SEL) = (void *)imp;
- id colorObject = getSemanticColorObject(klass, selector);
- if ([colorObject isKindOfClass:[UIColor class]]) {
- color = colorObject;
- } else if ([colorObject isKindOfClass:[NSArray class]]) {
- NSArray *colors = colorObject;
- NSNumber *index = colorInfo[RCTIndex];
- RCTAssert(index, @"index should not be null");
- color = colors[[index unsignedIntegerValue]];
- } else {
- RCTAssert(false, @"selector return an unknown object type");
- }
- }
- return color;
- }
- /** Returns an alphabetically sorted comma seperated list of the valid semantic color names
- */
- static NSString *RCTSemanticColorNames()
- {
- NSMutableString *names = [[NSMutableString alloc] init];
- NSDictionary<NSString *, NSDictionary *> *colorMap = RCTSemanticColorsMap();
- NSArray *allKeys =
- [[[colorMap allKeys] mutableCopy] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
- for (id key in allKeys) {
- if ([names length]) {
- [names appendString:@", "];
- }
- [names appendString:key];
- }
- return names;
- }
- + (UIColor *)UIColor:(id)json
- {
- if (!json) {
- return nil;
- }
- if ([json isKindOfClass:[NSArray class]]) {
- NSArray *components = [self NSNumberArray:json];
- CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0;
- return [UIColor colorWithRed:[self CGFloat:components[0]]
- green:[self CGFloat:components[1]]
- blue:[self CGFloat:components[2]]
- alpha:alpha];
- } else if ([json isKindOfClass:[NSNumber class]]) {
- NSUInteger argb = [self NSUInteger:json];
- CGFloat a = ((argb >> 24) & 0xFF) / 255.0;
- CGFloat r = ((argb >> 16) & 0xFF) / 255.0;
- CGFloat g = ((argb >> 8) & 0xFF) / 255.0;
- CGFloat b = (argb & 0xFF) / 255.0;
- return [UIColor colorWithRed:r green:g blue:b alpha:a];
- } else if ([json isKindOfClass:[NSDictionary class]]) {
- NSDictionary *dictionary = json;
- id value = nil;
- if ((value = [dictionary objectForKey:@"semantic"])) {
- if ([value isKindOfClass:[NSString class]]) {
- NSString *semanticName = value;
- UIColor *color = RCTColorFromSemanticColorName(semanticName);
- if (color == nil) {
- RCTLogConvertError(
- json,
- [@"a UIColor. Expected one of the following values: " stringByAppendingString:RCTSemanticColorNames()]);
- }
- return color;
- } else if ([value isKindOfClass:[NSArray class]]) {
- for (id name in value) {
- UIColor *color = RCTColorFromSemanticColorName(name);
- if (color != nil) {
- return color;
- }
- }
- RCTLogConvertError(
- json,
- [@"a UIColor. None of the names in the array were one of the following values: "
- stringByAppendingString:RCTSemanticColorNames()]);
- return nil;
- }
- RCTLogConvertError(
- json, @"a UIColor. Expected either a single name or an array of names but got something else.");
- return nil;
- } else if ((value = [dictionary objectForKey:@"dynamic"])) {
- NSDictionary *appearances = value;
- id light = [appearances objectForKey:@"light"];
- UIColor *lightColor = [RCTConvert UIColor:light];
- id dark = [appearances objectForKey:@"dark"];
- UIColor *darkColor = [RCTConvert UIColor:dark];
- if (lightColor != nil && darkColor != nil) {
- #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
- if (@available(iOS 13.0, *)) {
- UIColor *color =
- [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
- return collection.userInterfaceStyle == UIUserInterfaceStyleDark ? darkColor : lightColor;
- }];
- return color;
- } else {
- #endif
- return lightColor;
- #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
- }
- #endif
- } else {
- RCTLogConvertError(json, @"a UIColor. Expected an iOS dynamic appearance aware color.");
- return nil;
- }
- } else {
- RCTLogConvertError(json, @"a UIColor. Expected an iOS semantic color or dynamic appearance aware color.");
- return nil;
- }
- } else {
- RCTLogConvertError(json, @"a UIColor. Did you forget to call processColor() on the JS side?");
- return nil;
- }
- }
- + (CGColorRef)CGColor:(id)json
- {
- return [self UIColor:json].CGColor;
- }
- + (YGValue)YGValue:(id)json
- {
- if (!json) {
- return YGValueUndefined;
- } else if ([json isKindOfClass:[NSNumber class]]) {
- return (YGValue){[json floatValue], YGUnitPoint};
- } else if ([json isKindOfClass:[NSString class]]) {
- NSString *s = (NSString *)json;
- if ([s isEqualToString:@"auto"]) {
- return (YGValue){YGUndefined, YGUnitAuto};
- } else if ([s hasSuffix:@"%"]) {
- return (YGValue){[[s substringToIndex:s.length] floatValue], YGUnitPercent};
- } else {
- RCTLogConvertError(json, @"a YGValue. Did you forget the % or pt suffix?");
- }
- } else {
- RCTLogConvertError(json, @"a YGValue.");
- }
- return YGValueUndefined;
- }
- NSArray *RCTConvertArrayValue(SEL type, id json)
- {
- __block BOOL copy = NO;
- __block NSArray *values = json = [RCTConvert NSArray:json];
- [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
- id value = ((id(*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue);
- if (copy) {
- if (value) {
- [(NSMutableArray *)values addObject:value];
- }
- } else if (value != jsonValue) {
- // Converted value is different, so we'll need to copy the array
- values = [[NSMutableArray alloc] initWithCapacity:values.count];
- for (NSUInteger i = 0; i < idx; i++) {
- [(NSMutableArray *)values addObject:json[i]];
- }
- if (value) {
- [(NSMutableArray *)values addObject:value];
- }
- copy = YES;
- }
- }];
- return values;
- }
- RCT_ARRAY_CONVERTER(NSURL)
- RCT_ARRAY_CONVERTER(RCTFileURL)
- RCT_ARRAY_CONVERTER(UIColor)
- /**
- * This macro is used for creating converter functions for directly
- * representable json array values that require no conversion.
- */
- #if RCT_DEBUG
- #define RCT_JSON_ARRAY_CONVERTER_NAMED(type, name) RCT_ARRAY_CONVERTER_NAMED(type, name)
- #else
- #define RCT_JSON_ARRAY_CONVERTER_NAMED(type, name) \
- +(NSArray *)name##Array : (id)json \
- { \
- return json; \
- }
- #endif
- #define RCT_JSON_ARRAY_CONVERTER(type) RCT_JSON_ARRAY_CONVERTER_NAMED(type, type)
- RCT_JSON_ARRAY_CONVERTER(NSArray)
- RCT_JSON_ARRAY_CONVERTER(NSString)
- RCT_JSON_ARRAY_CONVERTER_NAMED(NSArray<NSString *>, NSStringArray)
- RCT_JSON_ARRAY_CONVERTER(NSDictionary)
- RCT_JSON_ARRAY_CONVERTER(NSNumber)
- // Can't use RCT_ARRAY_CONVERTER due to bridged cast
- + (NSArray *)CGColorArray:(id)json
- {
- NSMutableArray *colors = [NSMutableArray new];
- for (id value in [self NSArray:json]) {
- [colors addObject:(__bridge id)[self CGColor:value]];
- }
- return colors;
- }
- static id RCTConvertPropertyListValue(id json)
- {
- if (!json || json == (id)kCFNull) {
- return nil;
- }
- if ([json isKindOfClass:[NSDictionary class]]) {
- __block BOOL copy = NO;
- NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
- [json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, __unused BOOL *stop) {
- id value = RCTConvertPropertyListValue(jsonValue);
- if (value) {
- values[key] = value;
- }
- copy |= value != jsonValue;
- }];
- return copy ? values : json;
- }
- if ([json isKindOfClass:[NSArray class]]) {
- __block BOOL copy = NO;
- __block NSArray *values = json;
- [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
- id value = RCTConvertPropertyListValue(jsonValue);
- if (copy) {
- if (value) {
- [(NSMutableArray *)values addObject:value];
- }
- } else if (value != jsonValue) {
- // Converted value is different, so we'll need to copy the array
- values = [[NSMutableArray alloc] initWithCapacity:values.count];
- for (NSUInteger i = 0; i < idx; i++) {
- [(NSMutableArray *)values addObject:json[i]];
- }
- if (value) {
- [(NSMutableArray *)values addObject:value];
- }
- copy = YES;
- }
- }];
- return values;
- }
- // All other JSON types are supported by property lists
- return json;
- }
- + (NSPropertyList)NSPropertyList:(id)json
- {
- return RCTConvertPropertyListValue(json);
- }
- RCT_ENUM_CONVERTER(css_backface_visibility_t, (@{@"hidden" : @NO, @"visible" : @YES}), YES, boolValue)
- RCT_ENUM_CONVERTER(
- YGOverflow,
- (@{
- @"hidden" : @(YGOverflowHidden),
- @"visible" : @(YGOverflowVisible),
- @"scroll" : @(YGOverflowScroll),
- }),
- YGOverflowVisible,
- intValue)
- RCT_ENUM_CONVERTER(
- YGDisplay,
- (@{
- @"flex" : @(YGDisplayFlex),
- @"none" : @(YGDisplayNone),
- }),
- YGDisplayFlex,
- intValue)
- RCT_ENUM_CONVERTER(
- YGFlexDirection,
- (@{
- @"row" : @(YGFlexDirectionRow),
- @"row-reverse" : @(YGFlexDirectionRowReverse),
- @"column" : @(YGFlexDirectionColumn),
- @"column-reverse" : @(YGFlexDirectionColumnReverse)
- }),
- YGFlexDirectionColumn,
- intValue)
- RCT_ENUM_CONVERTER(
- YGJustify,
- (@{
- @"flex-start" : @(YGJustifyFlexStart),
- @"flex-end" : @(YGJustifyFlexEnd),
- @"center" : @(YGJustifyCenter),
- @"space-between" : @(YGJustifySpaceBetween),
- @"space-around" : @(YGJustifySpaceAround),
- @"space-evenly" : @(YGJustifySpaceEvenly)
- }),
- YGJustifyFlexStart,
- intValue)
- RCT_ENUM_CONVERTER(
- YGAlign,
- (@{
- @"flex-start" : @(YGAlignFlexStart),
- @"flex-end" : @(YGAlignFlexEnd),
- @"center" : @(YGAlignCenter),
- @"auto" : @(YGAlignAuto),
- @"stretch" : @(YGAlignStretch),
- @"baseline" : @(YGAlignBaseline),
- @"space-between" : @(YGAlignSpaceBetween),
- @"space-around" : @(YGAlignSpaceAround)
- }),
- YGAlignFlexStart,
- intValue)
- RCT_ENUM_CONVERTER(
- YGDirection,
- (@{
- @"inherit" : @(YGDirectionInherit),
- @"ltr" : @(YGDirectionLTR),
- @"rtl" : @(YGDirectionRTL),
- }),
- YGDirectionInherit,
- intValue)
- RCT_ENUM_CONVERTER(
- YGPositionType,
- (@{@"absolute" : @(YGPositionTypeAbsolute), @"relative" : @(YGPositionTypeRelative)}),
- YGPositionTypeRelative,
- intValue)
- RCT_ENUM_CONVERTER(
- YGWrap,
- (@{@"wrap" : @(YGWrapWrap), @"nowrap" : @(YGWrapNoWrap), @"wrap-reverse" : @(YGWrapWrapReverse)}),
- YGWrapNoWrap,
- intValue)
- RCT_ENUM_CONVERTER(
- RCTPointerEvents,
- (@{
- @"none" : @(RCTPointerEventsNone),
- @"box-only" : @(RCTPointerEventsBoxOnly),
- @"box-none" : @(RCTPointerEventsBoxNone),
- @"auto" : @(RCTPointerEventsUnspecified)
- }),
- RCTPointerEventsUnspecified,
- integerValue)
- RCT_ENUM_CONVERTER(
- RCTAnimationType,
- (@{
- @"spring" : @(RCTAnimationTypeSpring),
- @"linear" : @(RCTAnimationTypeLinear),
- @"easeIn" : @(RCTAnimationTypeEaseIn),
- @"easeOut" : @(RCTAnimationTypeEaseOut),
- @"easeInEaseOut" : @(RCTAnimationTypeEaseInEaseOut),
- @"keyboard" : @(RCTAnimationTypeKeyboard),
- }),
- RCTAnimationTypeEaseInEaseOut,
- integerValue)
- @end
- @interface RCTImageSource (Packager)
- @property (nonatomic, assign) BOOL packagerAsset;
- @end
- @implementation RCTConvert (Deprecated)
- /* This method is only used when loading images synchronously, e.g. for tabbar icons */
- + (UIImage *)UIImage:(id)json
- {
- if (!json) {
- return nil;
- }
- RCTImageSource *imageSource = [self RCTImageSource:json];
- if (!imageSource) {
- return nil;
- }
- __block UIImage *image;
- if (!RCTIsMainQueue()) {
- // It seems that none of the UIImage loading methods can be guaranteed
- // thread safe, so we'll pick the lesser of two evils here and block rather
- // than run the risk of crashing
- RCTLogWarn(@"Calling [RCTConvert UIImage:] on a background thread is not recommended");
- RCTUnsafeExecuteOnMainQueueSync(^{
- image = [self UIImage:json];
- });
- return image;
- }
- NSURL *URL = imageSource.request.URL;
- NSString *scheme = URL.scheme.lowercaseString;
- if ([scheme isEqualToString:@"file"]) {
- image = RCTImageFromLocalAssetURL(URL);
- // There is a case where this may fail when the image is at the bundle location.
- // RCTImageFromLocalAssetURL only checks for the image in the same location as the jsbundle
- // Hence, if the bundle is CodePush-ed, it will not be able to find the image.
- // This check is added here instead of being inside RCTImageFromLocalAssetURL, since
- // we don't want breaking changes to RCTImageFromLocalAssetURL, which is called in a lot of places
- // This is a deprecated method, and hence has the least impact on existing code. Basically,
- // instead of crashing the app, it tries one more location for the image.
- if (!image) {
- image = RCTImageFromLocalBundleAssetURL(URL);
- }
- if (!image) {
- RCTLogConvertError(json, @"an image. File not found.");
- }
- } else if ([scheme isEqualToString:@"data"]) {
- image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
- } else if ([scheme isEqualToString:@"http"] && imageSource.packagerAsset) {
- image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
- } else {
- RCTLogConvertError(json, @"an image. Only local files or data URIs are supported.");
- return nil;
- }
- CGFloat scale = imageSource.scale;
- if (!scale && imageSource.size.width) {
- // If no scale provided, set scale to image width / source width
- scale = CGImageGetWidth(image.CGImage) / imageSource.size.width;
- }
- if (scale) {
- image = [UIImage imageWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
- }
- if (!CGSizeEqualToSize(imageSource.size, CGSizeZero) && !CGSizeEqualToSize(imageSource.size, image.size)) {
- RCTLogError(
- @"Image source %@ size %@ does not match loaded image size %@.",
- URL.path.lastPathComponent,
- NSStringFromCGSize(imageSource.size),
- NSStringFromCGSize(image.size));
- }
- return image;
- }
- + (CGImageRef)CGImage:(id)json
- {
- return [self UIImage:json].CGImage;
- }
- @end
|