RCTConvert.m 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTConvert.h"
  8. #import <objc/message.h>
  9. #import <CoreText/CoreText.h>
  10. #import "RCTDefines.h"
  11. #import "RCTImageSource.h"
  12. #import "RCTParserUtils.h"
  13. #import "RCTUtils.h"
  14. @implementation RCTConvert
  15. RCT_CONVERTER(id, id, self)
  16. RCT_CONVERTER(BOOL, BOOL, boolValue)
  17. RCT_NUMBER_CONVERTER(double, doubleValue)
  18. RCT_NUMBER_CONVERTER(float, floatValue)
  19. RCT_NUMBER_CONVERTER(int, intValue)
  20. RCT_NUMBER_CONVERTER(int64_t, longLongValue);
  21. RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
  22. RCT_NUMBER_CONVERTER(NSInteger, integerValue)
  23. RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
  24. /**
  25. * This macro is used for creating converter functions for directly
  26. * representable json values that require no conversion.
  27. */
  28. #if RCT_DEBUG
  29. #define RCT_JSON_CONVERTER(type) \
  30. +(type *)type : (id)json \
  31. { \
  32. if ([json isKindOfClass:[type class]]) { \
  33. return json; \
  34. } else if (json) { \
  35. RCTLogConvertError(json, @ #type); \
  36. } \
  37. return nil; \
  38. }
  39. #else
  40. #define RCT_JSON_CONVERTER(type) \
  41. +(type *)type : (id)json \
  42. { \
  43. return json; \
  44. }
  45. #endif
  46. RCT_JSON_CONVERTER(NSArray)
  47. RCT_JSON_CONVERTER(NSDictionary)
  48. RCT_JSON_CONVERTER(NSString)
  49. RCT_JSON_CONVERTER(NSNumber)
  50. RCT_CUSTOM_CONVERTER(NSSet *, NSSet, [NSSet setWithArray:json])
  51. RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncoding])
  52. + (NSIndexSet *)NSIndexSet:(id)json
  53. {
  54. json = [self NSNumberArray:json];
  55. NSMutableIndexSet *indexSet = [NSMutableIndexSet new];
  56. for (NSNumber *number in json) {
  57. NSInteger index = number.integerValue;
  58. if (RCT_DEBUG && index < 0) {
  59. RCTLogError(@"Invalid index value %lld. Indices must be positive.", (long long)index);
  60. }
  61. [indexSet addIndex:index];
  62. }
  63. return indexSet;
  64. }
  65. + (NSURL *)NSURL:(id)json
  66. {
  67. NSString *path = [self NSString:RCTNilIfNull(json)];
  68. if (!path) {
  69. return nil;
  70. }
  71. @try { // NSURL has a history of crashing with bad input, so let's be safe
  72. NSURL *URL = [NSURL URLWithString:path];
  73. if (URL.scheme) { // Was a well-formed absolute URL
  74. return URL;
  75. }
  76. // Check if it has a scheme
  77. if ([path rangeOfString:@":"].location != NSNotFound) {
  78. NSMutableCharacterSet *urlAllowedCharacterSet = [NSMutableCharacterSet new];
  79. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLUserAllowedCharacterSet]];
  80. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPasswordAllowedCharacterSet]];
  81. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLHostAllowedCharacterSet]];
  82. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLPathAllowedCharacterSet]];
  83. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];
  84. [urlAllowedCharacterSet formUnionWithCharacterSet:[NSCharacterSet URLFragmentAllowedCharacterSet]];
  85. path = [path stringByAddingPercentEncodingWithAllowedCharacters:urlAllowedCharacterSet];
  86. URL = [NSURL URLWithString:path];
  87. if (URL) {
  88. return URL;
  89. }
  90. }
  91. // Assume that it's a local path
  92. path = path.stringByRemovingPercentEncoding;
  93. if ([path hasPrefix:@"~"]) {
  94. // Path is inside user directory
  95. path = path.stringByExpandingTildeInPath;
  96. } else if (!path.absolutePath) {
  97. // Assume it's a resource path
  98. path = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:path];
  99. }
  100. if (!(URL = [NSURL fileURLWithPath:path])) {
  101. RCTLogConvertError(json, @"a valid URL");
  102. }
  103. return URL;
  104. } @catch (__unused NSException *e) {
  105. RCTLogConvertError(json, @"a valid URL");
  106. return nil;
  107. }
  108. }
  109. RCT_ENUM_CONVERTER(
  110. NSURLRequestCachePolicy,
  111. (@{
  112. @"default" : @(NSURLRequestUseProtocolCachePolicy),
  113. @"reload" : @(NSURLRequestReloadIgnoringLocalCacheData),
  114. @"force-cache" : @(NSURLRequestReturnCacheDataElseLoad),
  115. @"only-if-cached" : @(NSURLRequestReturnCacheDataDontLoad),
  116. }),
  117. NSURLRequestUseProtocolCachePolicy,
  118. integerValue)
  119. + (NSURLRequest *)NSURLRequest:(id)json
  120. {
  121. if ([json isKindOfClass:[NSString class]]) {
  122. NSURL *URL = [self NSURL:json];
  123. return URL ? [NSURLRequest requestWithURL:URL] : nil;
  124. }
  125. if ([json isKindOfClass:[NSDictionary class]]) {
  126. NSString *URLString = json[@"uri"] ?: json[@"url"];
  127. NSURL *URL;
  128. NSString *bundleName = json[@"bundle"];
  129. if (bundleName) {
  130. URLString = [NSString stringWithFormat:@"%@.bundle/%@", bundleName, URLString];
  131. }
  132. URL = [self NSURL:URLString];
  133. if (!URL) {
  134. return nil;
  135. }
  136. NSData *body = [self NSData:json[@"body"]];
  137. NSString *method = [self NSString:json[@"method"]].uppercaseString ?: @"GET";
  138. NSURLRequestCachePolicy cachePolicy = [self NSURLRequestCachePolicy:json[@"cache"]];
  139. NSDictionary *headers = [self NSDictionary:json[@"headers"]];
  140. if ([method isEqualToString:@"GET"] && headers == nil && body == nil &&
  141. cachePolicy == NSURLRequestUseProtocolCachePolicy) {
  142. return [NSURLRequest requestWithURL:URL];
  143. }
  144. if (headers) {
  145. __block BOOL allHeadersAreStrings = YES;
  146. [headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id header, BOOL *stop) {
  147. if (![header isKindOfClass:[NSString class]]) {
  148. RCTLogError(
  149. @"Values of HTTP headers passed must be of type string. "
  150. "Value of header '%@' is not a string.",
  151. key);
  152. allHeadersAreStrings = NO;
  153. *stop = YES;
  154. }
  155. }];
  156. if (!allHeadersAreStrings) {
  157. // Set headers to nil here to avoid crashing later.
  158. headers = nil;
  159. }
  160. }
  161. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  162. request.HTTPBody = body;
  163. request.HTTPMethod = method;
  164. request.cachePolicy = cachePolicy;
  165. request.allHTTPHeaderFields = headers;
  166. return [request copy];
  167. }
  168. if (json) {
  169. RCTLogConvertError(json, @"a valid URLRequest");
  170. }
  171. return nil;
  172. }
  173. + (RCTFileURL *)RCTFileURL:(id)json
  174. {
  175. NSURL *fileURL = [self NSURL:json];
  176. if (!fileURL.fileURL) {
  177. RCTLogError(@"URI must be a local file, '%@' isn't.", fileURL);
  178. return nil;
  179. }
  180. if (![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
  181. RCTLogError(@"File '%@' could not be found.", fileURL);
  182. return nil;
  183. }
  184. return fileURL;
  185. }
  186. + (NSDate *)NSDate:(id)json
  187. {
  188. if ([json isKindOfClass:[NSNumber class]]) {
  189. return [NSDate dateWithTimeIntervalSince1970:[self NSTimeInterval:json]];
  190. } else if ([json isKindOfClass:[NSString class]]) {
  191. static NSDateFormatter *formatter;
  192. static dispatch_once_t onceToken;
  193. dispatch_once(&onceToken, ^{
  194. formatter = [NSDateFormatter new];
  195. formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
  196. formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
  197. formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
  198. });
  199. NSDate *date = [formatter dateFromString:json];
  200. if (!date) {
  201. RCTLogError(
  202. @"JSON String '%@' could not be interpreted as a date. "
  203. "Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ",
  204. json);
  205. }
  206. return date;
  207. } else if (json) {
  208. RCTLogConvertError(json, @"a date");
  209. }
  210. return nil;
  211. }
  212. + (NSLocale *)NSLocale:(id)json
  213. {
  214. if ([json isKindOfClass:[NSString class]]) {
  215. NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:json];
  216. if (!locale) {
  217. RCTLogError(@"JSON String '%@' could not be interpreted as a valid locale. ", json);
  218. }
  219. return locale;
  220. } else if (json) {
  221. RCTLogConvertError(json, @"a locale");
  222. }
  223. return nil;
  224. }
  225. // JS Standard for time is milliseconds
  226. RCT_CUSTOM_CONVERTER(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
  227. // JS standard for time zones is minutes.
  228. RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
  229. NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
  230. {
  231. if (!json) {
  232. return defaultValue;
  233. }
  234. if ([json isKindOfClass:[NSNumber class]]) {
  235. NSArray *allValues = mapping.allValues;
  236. if ([allValues containsObject:json] || [json isEqual:defaultValue]) {
  237. return json;
  238. }
  239. RCTLogError(@"Invalid %s '%@'. should be one of: %@", typeName, json, allValues);
  240. return defaultValue;
  241. }
  242. if (RCT_DEBUG && ![json isKindOfClass:[NSString class]]) {
  243. RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@", typeName, [json classForCoder], json);
  244. }
  245. id value = mapping[json];
  246. if (RCT_DEBUG && !value && [json description].length > 0) {
  247. RCTLogError(
  248. @"Invalid %s '%@'. should be one of: %@",
  249. typeName,
  250. json,
  251. [[mapping allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]);
  252. }
  253. return value ?: defaultValue;
  254. }
  255. NSNumber *RCTConvertMultiEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
  256. {
  257. if ([json isKindOfClass:[NSArray class]]) {
  258. if ([json count] == 0) {
  259. return defaultValue;
  260. }
  261. long long result = 0;
  262. for (id arrayElement in json) {
  263. NSNumber *value = RCTConvertEnumValue(typeName, mapping, defaultValue, arrayElement);
  264. result |= value.longLongValue;
  265. }
  266. return @(result);
  267. }
  268. return RCTConvertEnumValue(typeName, mapping, defaultValue, json);
  269. }
  270. RCT_ENUM_CONVERTER(
  271. NSLineBreakMode,
  272. (@{
  273. @"clip" : @(NSLineBreakByClipping),
  274. @"head" : @(NSLineBreakByTruncatingHead),
  275. @"tail" : @(NSLineBreakByTruncatingTail),
  276. @"middle" : @(NSLineBreakByTruncatingMiddle),
  277. @"wordWrapping" : @(NSLineBreakByWordWrapping),
  278. }),
  279. NSLineBreakByTruncatingTail,
  280. integerValue)
  281. RCT_ENUM_CONVERTER(
  282. NSTextAlignment,
  283. (@{
  284. @"auto" : @(NSTextAlignmentNatural),
  285. @"left" : @(NSTextAlignmentLeft),
  286. @"center" : @(NSTextAlignmentCenter),
  287. @"right" : @(NSTextAlignmentRight),
  288. @"justify" : @(NSTextAlignmentJustified),
  289. }),
  290. NSTextAlignmentNatural,
  291. integerValue)
  292. RCT_ENUM_CONVERTER(
  293. NSUnderlineStyle,
  294. (@{
  295. @"solid" : @(NSUnderlineStyleSingle),
  296. @"double" : @(NSUnderlineStyleDouble),
  297. @"dotted" : @(NSUnderlinePatternDot | NSUnderlineStyleSingle),
  298. @"dashed" : @(NSUnderlinePatternDash | NSUnderlineStyleSingle),
  299. }),
  300. NSUnderlineStyleSingle,
  301. integerValue)
  302. RCT_ENUM_CONVERTER(
  303. RCTBorderStyle,
  304. (@{
  305. @"solid" : @(RCTBorderStyleSolid),
  306. @"dotted" : @(RCTBorderStyleDotted),
  307. @"dashed" : @(RCTBorderStyleDashed),
  308. }),
  309. RCTBorderStyleSolid,
  310. integerValue)
  311. RCT_ENUM_CONVERTER(
  312. RCTTextDecorationLineType,
  313. (@{
  314. @"none" : @(RCTTextDecorationLineTypeNone),
  315. @"underline" : @(RCTTextDecorationLineTypeUnderline),
  316. @"line-through" : @(RCTTextDecorationLineTypeStrikethrough),
  317. @"underline line-through" : @(RCTTextDecorationLineTypeUnderlineStrikethrough),
  318. }),
  319. RCTTextDecorationLineTypeNone,
  320. integerValue)
  321. RCT_ENUM_CONVERTER(
  322. NSWritingDirection,
  323. (@{
  324. @"auto" : @(NSWritingDirectionNatural),
  325. @"ltr" : @(NSWritingDirectionLeftToRight),
  326. @"rtl" : @(NSWritingDirectionRightToLeft),
  327. }),
  328. NSWritingDirectionNatural,
  329. integerValue)
  330. RCT_ENUM_CONVERTER(
  331. UITextAutocapitalizationType,
  332. (@{
  333. @"none" : @(UITextAutocapitalizationTypeNone),
  334. @"words" : @(UITextAutocapitalizationTypeWords),
  335. @"sentences" : @(UITextAutocapitalizationTypeSentences),
  336. @"characters" : @(UITextAutocapitalizationTypeAllCharacters)
  337. }),
  338. UITextAutocapitalizationTypeSentences,
  339. integerValue)
  340. RCT_ENUM_CONVERTER(
  341. UITextFieldViewMode,
  342. (@{
  343. @"never" : @(UITextFieldViewModeNever),
  344. @"while-editing" : @(UITextFieldViewModeWhileEditing),
  345. @"unless-editing" : @(UITextFieldViewModeUnlessEditing),
  346. @"always" : @(UITextFieldViewModeAlways),
  347. }),
  348. UITextFieldViewModeNever,
  349. integerValue)
  350. + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC
  351. {
  352. static NSDictionary<NSString *, NSNumber *> *mapping;
  353. static dispatch_once_t onceToken;
  354. dispatch_once(&onceToken, ^{
  355. NSMutableDictionary<NSString *, NSNumber *> *temporaryMapping = [NSMutableDictionary dictionaryWithDictionary:@{
  356. @"default" : @(UIKeyboardTypeDefault),
  357. @"ascii-capable" : @(UIKeyboardTypeASCIICapable),
  358. @"numbers-and-punctuation" : @(UIKeyboardTypeNumbersAndPunctuation),
  359. @"url" : @(UIKeyboardTypeURL),
  360. @"number-pad" : @(UIKeyboardTypeNumberPad),
  361. @"phone-pad" : @(UIKeyboardTypePhonePad),
  362. @"name-phone-pad" : @(UIKeyboardTypeNamePhonePad),
  363. @"email-address" : @(UIKeyboardTypeEmailAddress),
  364. @"decimal-pad" : @(UIKeyboardTypeDecimalPad),
  365. @"twitter" : @(UIKeyboardTypeTwitter),
  366. @"web-search" : @(UIKeyboardTypeWebSearch),
  367. // Added for Android compatibility
  368. @"numeric" : @(UIKeyboardTypeDecimalPad),
  369. }];
  370. temporaryMapping[@"ascii-capable-number-pad"] = @(UIKeyboardTypeASCIICapableNumberPad);
  371. mapping = temporaryMapping;
  372. });
  373. UIKeyboardType type = RCTConvertEnumValue("UIKeyboardType", mapping, @(UIKeyboardTypeDefault), json).integerValue;
  374. return type;
  375. }
  376. #if !TARGET_OS_TV
  377. RCT_MULTI_ENUM_CONVERTER(
  378. UIDataDetectorTypes,
  379. (@{
  380. @"phoneNumber" : @(UIDataDetectorTypePhoneNumber),
  381. @"link" : @(UIDataDetectorTypeLink),
  382. @"address" : @(UIDataDetectorTypeAddress),
  383. @"calendarEvent" : @(UIDataDetectorTypeCalendarEvent),
  384. @"none" : @(UIDataDetectorTypeNone),
  385. @"all" : @(UIDataDetectorTypeAll),
  386. }),
  387. UIDataDetectorTypePhoneNumber,
  388. unsignedLongLongValue)
  389. #if WEBKIT_IOS_10_APIS_AVAILABLE
  390. RCT_MULTI_ENUM_CONVERTER(
  391. WKDataDetectorTypes,
  392. (@{
  393. @"phoneNumber" : @(WKDataDetectorTypePhoneNumber),
  394. @"link" : @(WKDataDetectorTypeLink),
  395. @"address" : @(WKDataDetectorTypeAddress),
  396. @"calendarEvent" : @(WKDataDetectorTypeCalendarEvent),
  397. @"trackingNumber" : @(WKDataDetectorTypeTrackingNumber),
  398. @"flightNumber" : @(WKDataDetectorTypeFlightNumber),
  399. @"lookupSuggestion" : @(WKDataDetectorTypeLookupSuggestion),
  400. @"none" : @(WKDataDetectorTypeNone),
  401. @"all" : @(WKDataDetectorTypeAll),
  402. }),
  403. WKDataDetectorTypePhoneNumber,
  404. unsignedLongLongValue)
  405. #endif // WEBKIT_IOS_10_APIS_AVAILABLE
  406. #endif // !TARGET_OS_TV
  407. RCT_ENUM_CONVERTER(
  408. UIKeyboardAppearance,
  409. (@{
  410. @"default" : @(UIKeyboardAppearanceDefault),
  411. @"light" : @(UIKeyboardAppearanceLight),
  412. @"dark" : @(UIKeyboardAppearanceDark),
  413. }),
  414. UIKeyboardAppearanceDefault,
  415. integerValue)
  416. RCT_ENUM_CONVERTER(
  417. UIReturnKeyType,
  418. (@{
  419. @"default" : @(UIReturnKeyDefault),
  420. @"go" : @(UIReturnKeyGo),
  421. @"google" : @(UIReturnKeyGoogle),
  422. @"join" : @(UIReturnKeyJoin),
  423. @"next" : @(UIReturnKeyNext),
  424. @"route" : @(UIReturnKeyRoute),
  425. @"search" : @(UIReturnKeySearch),
  426. @"send" : @(UIReturnKeySend),
  427. @"yahoo" : @(UIReturnKeyYahoo),
  428. @"done" : @(UIReturnKeyDone),
  429. @"emergency-call" : @(UIReturnKeyEmergencyCall),
  430. }),
  431. UIReturnKeyDefault,
  432. integerValue)
  433. RCT_ENUM_CONVERTER(
  434. UIViewContentMode,
  435. (@{
  436. @"scale-to-fill" : @(UIViewContentModeScaleToFill),
  437. @"scale-aspect-fit" : @(UIViewContentModeScaleAspectFit),
  438. @"scale-aspect-fill" : @(UIViewContentModeScaleAspectFill),
  439. @"redraw" : @(UIViewContentModeRedraw),
  440. @"center" : @(UIViewContentModeCenter),
  441. @"top" : @(UIViewContentModeTop),
  442. @"bottom" : @(UIViewContentModeBottom),
  443. @"left" : @(UIViewContentModeLeft),
  444. @"right" : @(UIViewContentModeRight),
  445. @"top-left" : @(UIViewContentModeTopLeft),
  446. @"top-right" : @(UIViewContentModeTopRight),
  447. @"bottom-left" : @(UIViewContentModeBottomLeft),
  448. @"bottom-right" : @(UIViewContentModeBottomRight),
  449. // Cross-platform values
  450. @"cover" : @(UIViewContentModeScaleAspectFill),
  451. @"contain" : @(UIViewContentModeScaleAspectFit),
  452. @"stretch" : @(UIViewContentModeScaleToFill),
  453. }),
  454. UIViewContentModeScaleAspectFill,
  455. integerValue)
  456. #if !TARGET_OS_TV
  457. RCT_ENUM_CONVERTER(
  458. UIBarStyle,
  459. (@{
  460. @"default" : @(UIBarStyleDefault),
  461. @"black" : @(UIBarStyleBlack),
  462. @"blackOpaque" : @(UIBarStyleBlackOpaque),
  463. @"blackTranslucent" : @(UIBarStyleBlackTranslucent),
  464. }),
  465. UIBarStyleDefault,
  466. integerValue)
  467. #endif
  468. static void convertCGStruct(const char *type, NSArray *fields, CGFloat *result, id json)
  469. {
  470. NSUInteger count = fields.count;
  471. if ([json isKindOfClass:[NSArray class]]) {
  472. if (RCT_DEBUG && [json count] != count) {
  473. RCTLogError(
  474. @"Expected array with count %llu, but count is %llu: %@",
  475. (unsigned long long)count,
  476. (unsigned long long)[json count],
  477. json);
  478. } else {
  479. for (NSUInteger i = 0; i < count; i++) {
  480. result[i] = [RCTConvert CGFloat:RCTNilIfNull(json[i])];
  481. }
  482. }
  483. } else if ([json isKindOfClass:[NSDictionary class]]) {
  484. for (NSUInteger i = 0; i < count; i++) {
  485. result[i] = [RCTConvert CGFloat:RCTNilIfNull(json[fields[i]])];
  486. }
  487. } else if (json) {
  488. RCTLogConvertError(json, @(type));
  489. }
  490. }
  491. /**
  492. * This macro is used for creating converter functions for structs that consist
  493. * of a number of CGFloat properties, such as CGPoint, CGRect, etc.
  494. */
  495. #define RCT_CGSTRUCT_CONVERTER(type, values) \
  496. +(type)type : (id)json \
  497. { \
  498. static NSArray *fields; \
  499. static dispatch_once_t onceToken; \
  500. dispatch_once(&onceToken, ^{ \
  501. fields = values; \
  502. }); \
  503. type result; \
  504. convertCGStruct(#type, fields, (CGFloat *)&result, json); \
  505. return result; \
  506. }
  507. RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json])
  508. RCT_CGSTRUCT_CONVERTER(CGPoint, (@[ @"x", @"y" ]))
  509. RCT_CGSTRUCT_CONVERTER(CGSize, (@[ @"width", @"height" ]))
  510. RCT_CGSTRUCT_CONVERTER(CGRect, (@[ @"x", @"y", @"width", @"height" ]))
  511. RCT_CGSTRUCT_CONVERTER(UIEdgeInsets, (@[ @"top", @"left", @"bottom", @"right" ]))
  512. RCT_ENUM_CONVERTER(
  513. CGLineJoin,
  514. (@{
  515. @"miter" : @(kCGLineJoinMiter),
  516. @"round" : @(kCGLineJoinRound),
  517. @"bevel" : @(kCGLineJoinBevel),
  518. }),
  519. kCGLineJoinMiter,
  520. intValue)
  521. RCT_ENUM_CONVERTER(
  522. CGLineCap,
  523. (@{
  524. @"butt" : @(kCGLineCapButt),
  525. @"round" : @(kCGLineCapRound),
  526. @"square" : @(kCGLineCapSquare),
  527. }),
  528. kCGLineCapButt,
  529. intValue)
  530. RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[ @"a", @"b", @"c", @"d", @"tx", @"ty" ]))
  531. static NSString *const RCTFallback = @"fallback";
  532. static NSString *const RCTFallbackARGB = @"fallback-argb";
  533. static NSString *const RCTSelector = @"selector";
  534. static NSString *const RCTIndex = @"index";
  535. /** The following dictionary defines the react-native semantic colors for ios.
  536. * If the value for a given name is empty then the name itself
  537. * is used as the UIColor selector.
  538. * If the RCTSelector key is present then that value is used for a selector instead
  539. * of the key name.
  540. * If the given selector is not available on the running OS version then
  541. * the RCTFallback selector is used instead.
  542. * If the RCTIndex key is present then object returned from UIColor is an
  543. * NSArray and the object at index RCTIndex is to be used.
  544. */
  545. static NSDictionary<NSString *, NSDictionary *> *RCTSemanticColorsMap()
  546. {
  547. static NSDictionary<NSString *, NSDictionary *> *colorMap = nil;
  548. if (colorMap == nil) {
  549. NSMutableDictionary<NSString *, NSDictionary *> *map = [@{
  550. // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors
  551. // Label Colors
  552. @"labelColor" : @{
  553. // iOS 13.0
  554. RCTFallbackARGB :
  555. @(0xFF000000) // fallback for iOS<=12: RGBA returned by this semantic color in light mode on iOS 13
  556. },
  557. @"secondaryLabelColor" : @{
  558. // iOS 13.0
  559. RCTFallbackARGB : @(0x993c3c43)
  560. },
  561. @"tertiaryLabelColor" : @{
  562. // iOS 13.0
  563. RCTFallbackARGB : @(0x4c3c3c43)
  564. },
  565. @"quaternaryLabelColor" : @{
  566. // iOS 13.0
  567. RCTFallbackARGB : @(0x2d3c3c43)
  568. },
  569. // Fill Colors
  570. @"systemFillColor" : @{
  571. // iOS 13.0
  572. RCTFallbackARGB : @(0x33787880)
  573. },
  574. @"secondarySystemFillColor" : @{
  575. // iOS 13.0
  576. RCTFallbackARGB : @(0x28787880)
  577. },
  578. @"tertiarySystemFillColor" : @{
  579. // iOS 13.0
  580. RCTFallbackARGB : @(0x1e767680)
  581. },
  582. @"quaternarySystemFillColor" : @{
  583. // iOS 13.0
  584. RCTFallbackARGB : @(0x14747480)
  585. },
  586. // Text Colors
  587. @"placeholderTextColor" : @{
  588. // iOS 13.0
  589. RCTFallbackARGB : @(0x4c3c3c43)
  590. },
  591. // Standard Content Background Colors
  592. @"systemBackgroundColor" : @{
  593. // iOS 13.0
  594. RCTFallbackARGB : @(0xFFffffff)
  595. },
  596. @"secondarySystemBackgroundColor" : @{
  597. // iOS 13.0
  598. RCTFallbackARGB : @(0xFFf2f2f7)
  599. },
  600. @"tertiarySystemBackgroundColor" : @{
  601. // iOS 13.0
  602. RCTFallbackARGB : @(0xFFffffff)
  603. },
  604. // Grouped Content Background Colors
  605. @"systemGroupedBackgroundColor" : @{
  606. // iOS 13.0
  607. RCTFallbackARGB : @(0xFFf2f2f7)
  608. },
  609. @"secondarySystemGroupedBackgroundColor" : @{
  610. // iOS 13.0
  611. RCTFallbackARGB : @(0xFFffffff)
  612. },
  613. @"tertiarySystemGroupedBackgroundColor" : @{
  614. // iOS 13.0
  615. RCTFallbackARGB : @(0xFFf2f2f7)
  616. },
  617. // Separator Colors
  618. @"separatorColor" : @{
  619. // iOS 13.0
  620. RCTFallbackARGB : @(0x493c3c43)
  621. },
  622. @"opaqueSeparatorColor" : @{
  623. // iOS 13.0
  624. RCTFallbackARGB : @(0xFFc6c6c8)
  625. },
  626. // Link Color
  627. @"linkColor" : @{
  628. // iOS 13.0
  629. RCTFallbackARGB : @(0xFF007aff)
  630. },
  631. // Nonadaptable Colors
  632. @"darkTextColor" : @{},
  633. @"lightTextColor" : @{},
  634. // https://developer.apple.com/documentation/uikit/uicolor/standard_colors
  635. // Adaptable Colors
  636. @"systemBlueColor" : @{},
  637. @"systemBrownColor" : @{
  638. // iOS 13.0
  639. RCTFallbackARGB : @(0xFFa2845e)
  640. },
  641. @"systemGreenColor" : @{},
  642. @"systemIndigoColor" : @{
  643. // iOS 13.0
  644. RCTFallbackARGB : @(0xFF5856d6)
  645. },
  646. @"systemOrangeColor" : @{},
  647. @"systemPinkColor" : @{},
  648. @"systemPurpleColor" : @{},
  649. @"systemRedColor" : @{},
  650. @"systemTealColor" : @{},
  651. @"systemYellowColor" : @{},
  652. // Adaptable Gray Colors
  653. @"systemGrayColor" : @{},
  654. @"systemGray2Color" : @{
  655. // iOS 13.0
  656. RCTFallbackARGB : @(0xFFaeaeb2)
  657. },
  658. @"systemGray3Color" : @{
  659. // iOS 13.0
  660. RCTFallbackARGB : @(0xFFc7c7cc)
  661. },
  662. @"systemGray4Color" : @{
  663. // iOS 13.0
  664. RCTFallbackARGB : @(0xFFd1d1d6)
  665. },
  666. @"systemGray5Color" : @{
  667. // iOS 13.0
  668. RCTFallbackARGB : @(0xFFe5e5ea)
  669. },
  670. @"systemGray6Color" : @{
  671. // iOS 13.0
  672. RCTFallbackARGB : @(0xFFf2f2f7)
  673. },
  674. } mutableCopy];
  675. // The color names are the Objective-C UIColor selector names,
  676. // but Swift selector names are valid as well, so make aliases.
  677. static NSString *const RCTColorSuffix = @"Color";
  678. NSMutableDictionary<NSString *, NSDictionary *> *aliases = [NSMutableDictionary new];
  679. for (NSString *objcSelector in map) {
  680. RCTAssert([objcSelector hasSuffix:RCTColorSuffix], @"A selector in the color map did not end with the suffix Color.");
  681. NSMutableDictionary *entry = [map[objcSelector] mutableCopy];
  682. RCTAssert([entry objectForKey:RCTSelector] == nil, @"Entry should not already have an RCTSelector");
  683. NSString *swiftSelector = [objcSelector substringToIndex:[objcSelector length] - [RCTColorSuffix length]];
  684. entry[RCTSelector] = objcSelector;
  685. aliases[swiftSelector] = entry;
  686. }
  687. [map addEntriesFromDictionary:aliases];
  688. #if DEBUG
  689. [map addEntriesFromDictionary:@{
  690. // The follow exist for Unit Tests
  691. @"unitTestFallbackColor" : @{RCTFallback : @"gridColor"},
  692. @"unitTestFallbackColorIOS" : @{RCTFallback : @"blueColor"},
  693. @"unitTestFallbackColorEven" : @{
  694. RCTSelector : @"unitTestFallbackColorEven",
  695. RCTIndex : @0,
  696. RCTFallback : @"controlAlternatingRowBackgroundColors"
  697. },
  698. @"unitTestFallbackColorOdd" : @{
  699. RCTSelector : @"unitTestFallbackColorOdd",
  700. RCTIndex : @1,
  701. RCTFallback : @"controlAlternatingRowBackgroundColors"
  702. },
  703. }];
  704. #endif
  705. colorMap = [map copy];
  706. }
  707. return colorMap;
  708. }
  709. /** Returns a UIColor based on a semantic color name.
  710. * Returns nil if the semantic color name is invalid.
  711. */
  712. static UIColor *RCTColorFromSemanticColorName(NSString *semanticColorName)
  713. {
  714. NSDictionary<NSString *, NSDictionary *> *colorMap = RCTSemanticColorsMap();
  715. UIColor *color = nil;
  716. NSDictionary<NSString *, id> *colorInfo = colorMap[semanticColorName];
  717. if (colorInfo) {
  718. NSString *semanticColorSelector = colorInfo[RCTSelector];
  719. if (semanticColorSelector == nil) {
  720. semanticColorSelector = semanticColorName;
  721. }
  722. SEL selector = NSSelectorFromString(semanticColorSelector);
  723. if (![UIColor respondsToSelector:selector]) {
  724. NSNumber *fallbackRGB = colorInfo[RCTFallbackARGB];
  725. if (fallbackRGB != nil) {
  726. RCTAssert([fallbackRGB isKindOfClass:[NSNumber class]], @"fallback ARGB is not a number");
  727. return [RCTConvert UIColor:fallbackRGB];
  728. }
  729. semanticColorSelector = colorInfo[RCTFallback];
  730. selector = NSSelectorFromString(semanticColorSelector);
  731. }
  732. RCTAssert([UIColor respondsToSelector:selector], @"RCTUIColor does not respond to a semantic color selector.");
  733. Class klass = [UIColor class];
  734. IMP imp = [klass methodForSelector:selector];
  735. id (*getSemanticColorObject)(id, SEL) = (void *)imp;
  736. id colorObject = getSemanticColorObject(klass, selector);
  737. if ([colorObject isKindOfClass:[UIColor class]]) {
  738. color = colorObject;
  739. } else if ([colorObject isKindOfClass:[NSArray class]]) {
  740. NSArray *colors = colorObject;
  741. NSNumber *index = colorInfo[RCTIndex];
  742. RCTAssert(index, @"index should not be null");
  743. color = colors[[index unsignedIntegerValue]];
  744. } else {
  745. RCTAssert(false, @"selector return an unknown object type");
  746. }
  747. }
  748. return color;
  749. }
  750. /** Returns an alphabetically sorted comma seperated list of the valid semantic color names
  751. */
  752. static NSString *RCTSemanticColorNames()
  753. {
  754. NSMutableString *names = [[NSMutableString alloc] init];
  755. NSDictionary<NSString *, NSDictionary *> *colorMap = RCTSemanticColorsMap();
  756. NSArray *allKeys =
  757. [[[colorMap allKeys] mutableCopy] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
  758. for (id key in allKeys) {
  759. if ([names length]) {
  760. [names appendString:@", "];
  761. }
  762. [names appendString:key];
  763. }
  764. return names;
  765. }
  766. + (UIColor *)UIColor:(id)json
  767. {
  768. if (!json) {
  769. return nil;
  770. }
  771. if ([json isKindOfClass:[NSArray class]]) {
  772. NSArray *components = [self NSNumberArray:json];
  773. CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0;
  774. return [UIColor colorWithRed:[self CGFloat:components[0]]
  775. green:[self CGFloat:components[1]]
  776. blue:[self CGFloat:components[2]]
  777. alpha:alpha];
  778. } else if ([json isKindOfClass:[NSNumber class]]) {
  779. NSUInteger argb = [self NSUInteger:json];
  780. CGFloat a = ((argb >> 24) & 0xFF) / 255.0;
  781. CGFloat r = ((argb >> 16) & 0xFF) / 255.0;
  782. CGFloat g = ((argb >> 8) & 0xFF) / 255.0;
  783. CGFloat b = (argb & 0xFF) / 255.0;
  784. return [UIColor colorWithRed:r green:g blue:b alpha:a];
  785. } else if ([json isKindOfClass:[NSDictionary class]]) {
  786. NSDictionary *dictionary = json;
  787. id value = nil;
  788. if ((value = [dictionary objectForKey:@"semantic"])) {
  789. if ([value isKindOfClass:[NSString class]]) {
  790. NSString *semanticName = value;
  791. UIColor *color = RCTColorFromSemanticColorName(semanticName);
  792. if (color == nil) {
  793. RCTLogConvertError(
  794. json,
  795. [@"a UIColor. Expected one of the following values: " stringByAppendingString:RCTSemanticColorNames()]);
  796. }
  797. return color;
  798. } else if ([value isKindOfClass:[NSArray class]]) {
  799. for (id name in value) {
  800. UIColor *color = RCTColorFromSemanticColorName(name);
  801. if (color != nil) {
  802. return color;
  803. }
  804. }
  805. RCTLogConvertError(
  806. json,
  807. [@"a UIColor. None of the names in the array were one of the following values: "
  808. stringByAppendingString:RCTSemanticColorNames()]);
  809. return nil;
  810. }
  811. RCTLogConvertError(
  812. json, @"a UIColor. Expected either a single name or an array of names but got something else.");
  813. return nil;
  814. } else if ((value = [dictionary objectForKey:@"dynamic"])) {
  815. NSDictionary *appearances = value;
  816. id light = [appearances objectForKey:@"light"];
  817. UIColor *lightColor = [RCTConvert UIColor:light];
  818. id dark = [appearances objectForKey:@"dark"];
  819. UIColor *darkColor = [RCTConvert UIColor:dark];
  820. if (lightColor != nil && darkColor != nil) {
  821. #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  822. if (@available(iOS 13.0, *)) {
  823. UIColor *color =
  824. [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
  825. return collection.userInterfaceStyle == UIUserInterfaceStyleDark ? darkColor : lightColor;
  826. }];
  827. return color;
  828. } else {
  829. #endif
  830. return lightColor;
  831. #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  832. }
  833. #endif
  834. } else {
  835. RCTLogConvertError(json, @"a UIColor. Expected an iOS dynamic appearance aware color.");
  836. return nil;
  837. }
  838. } else {
  839. RCTLogConvertError(json, @"a UIColor. Expected an iOS semantic color or dynamic appearance aware color.");
  840. return nil;
  841. }
  842. } else {
  843. RCTLogConvertError(json, @"a UIColor. Did you forget to call processColor() on the JS side?");
  844. return nil;
  845. }
  846. }
  847. + (CGColorRef)CGColor:(id)json
  848. {
  849. return [self UIColor:json].CGColor;
  850. }
  851. + (YGValue)YGValue:(id)json
  852. {
  853. if (!json) {
  854. return YGValueUndefined;
  855. } else if ([json isKindOfClass:[NSNumber class]]) {
  856. return (YGValue){[json floatValue], YGUnitPoint};
  857. } else if ([json isKindOfClass:[NSString class]]) {
  858. NSString *s = (NSString *)json;
  859. if ([s isEqualToString:@"auto"]) {
  860. return (YGValue){YGUndefined, YGUnitAuto};
  861. } else if ([s hasSuffix:@"%"]) {
  862. return (YGValue){[[s substringToIndex:s.length] floatValue], YGUnitPercent};
  863. } else {
  864. RCTLogConvertError(json, @"a YGValue. Did you forget the % or pt suffix?");
  865. }
  866. } else {
  867. RCTLogConvertError(json, @"a YGValue.");
  868. }
  869. return YGValueUndefined;
  870. }
  871. NSArray *RCTConvertArrayValue(SEL type, id json)
  872. {
  873. __block BOOL copy = NO;
  874. __block NSArray *values = json = [RCTConvert NSArray:json];
  875. [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
  876. id value = ((id(*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue);
  877. if (copy) {
  878. if (value) {
  879. [(NSMutableArray *)values addObject:value];
  880. }
  881. } else if (value != jsonValue) {
  882. // Converted value is different, so we'll need to copy the array
  883. values = [[NSMutableArray alloc] initWithCapacity:values.count];
  884. for (NSUInteger i = 0; i < idx; i++) {
  885. [(NSMutableArray *)values addObject:json[i]];
  886. }
  887. if (value) {
  888. [(NSMutableArray *)values addObject:value];
  889. }
  890. copy = YES;
  891. }
  892. }];
  893. return values;
  894. }
  895. RCT_ARRAY_CONVERTER(NSURL)
  896. RCT_ARRAY_CONVERTER(RCTFileURL)
  897. RCT_ARRAY_CONVERTER(UIColor)
  898. /**
  899. * This macro is used for creating converter functions for directly
  900. * representable json array values that require no conversion.
  901. */
  902. #if RCT_DEBUG
  903. #define RCT_JSON_ARRAY_CONVERTER_NAMED(type, name) RCT_ARRAY_CONVERTER_NAMED(type, name)
  904. #else
  905. #define RCT_JSON_ARRAY_CONVERTER_NAMED(type, name) \
  906. +(NSArray *)name##Array : (id)json \
  907. { \
  908. return json; \
  909. }
  910. #endif
  911. #define RCT_JSON_ARRAY_CONVERTER(type) RCT_JSON_ARRAY_CONVERTER_NAMED(type, type)
  912. RCT_JSON_ARRAY_CONVERTER(NSArray)
  913. RCT_JSON_ARRAY_CONVERTER(NSString)
  914. RCT_JSON_ARRAY_CONVERTER_NAMED(NSArray<NSString *>, NSStringArray)
  915. RCT_JSON_ARRAY_CONVERTER(NSDictionary)
  916. RCT_JSON_ARRAY_CONVERTER(NSNumber)
  917. // Can't use RCT_ARRAY_CONVERTER due to bridged cast
  918. + (NSArray *)CGColorArray:(id)json
  919. {
  920. NSMutableArray *colors = [NSMutableArray new];
  921. for (id value in [self NSArray:json]) {
  922. [colors addObject:(__bridge id)[self CGColor:value]];
  923. }
  924. return colors;
  925. }
  926. static id RCTConvertPropertyListValue(id json)
  927. {
  928. if (!json || json == (id)kCFNull) {
  929. return nil;
  930. }
  931. if ([json isKindOfClass:[NSDictionary class]]) {
  932. __block BOOL copy = NO;
  933. NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
  934. [json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, __unused BOOL *stop) {
  935. id value = RCTConvertPropertyListValue(jsonValue);
  936. if (value) {
  937. values[key] = value;
  938. }
  939. copy |= value != jsonValue;
  940. }];
  941. return copy ? values : json;
  942. }
  943. if ([json isKindOfClass:[NSArray class]]) {
  944. __block BOOL copy = NO;
  945. __block NSArray *values = json;
  946. [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
  947. id value = RCTConvertPropertyListValue(jsonValue);
  948. if (copy) {
  949. if (value) {
  950. [(NSMutableArray *)values addObject:value];
  951. }
  952. } else if (value != jsonValue) {
  953. // Converted value is different, so we'll need to copy the array
  954. values = [[NSMutableArray alloc] initWithCapacity:values.count];
  955. for (NSUInteger i = 0; i < idx; i++) {
  956. [(NSMutableArray *)values addObject:json[i]];
  957. }
  958. if (value) {
  959. [(NSMutableArray *)values addObject:value];
  960. }
  961. copy = YES;
  962. }
  963. }];
  964. return values;
  965. }
  966. // All other JSON types are supported by property lists
  967. return json;
  968. }
  969. + (NSPropertyList)NSPropertyList:(id)json
  970. {
  971. return RCTConvertPropertyListValue(json);
  972. }
  973. RCT_ENUM_CONVERTER(css_backface_visibility_t, (@{@"hidden" : @NO, @"visible" : @YES}), YES, boolValue)
  974. RCT_ENUM_CONVERTER(
  975. YGOverflow,
  976. (@{
  977. @"hidden" : @(YGOverflowHidden),
  978. @"visible" : @(YGOverflowVisible),
  979. @"scroll" : @(YGOverflowScroll),
  980. }),
  981. YGOverflowVisible,
  982. intValue)
  983. RCT_ENUM_CONVERTER(
  984. YGDisplay,
  985. (@{
  986. @"flex" : @(YGDisplayFlex),
  987. @"none" : @(YGDisplayNone),
  988. }),
  989. YGDisplayFlex,
  990. intValue)
  991. RCT_ENUM_CONVERTER(
  992. YGFlexDirection,
  993. (@{
  994. @"row" : @(YGFlexDirectionRow),
  995. @"row-reverse" : @(YGFlexDirectionRowReverse),
  996. @"column" : @(YGFlexDirectionColumn),
  997. @"column-reverse" : @(YGFlexDirectionColumnReverse)
  998. }),
  999. YGFlexDirectionColumn,
  1000. intValue)
  1001. RCT_ENUM_CONVERTER(
  1002. YGJustify,
  1003. (@{
  1004. @"flex-start" : @(YGJustifyFlexStart),
  1005. @"flex-end" : @(YGJustifyFlexEnd),
  1006. @"center" : @(YGJustifyCenter),
  1007. @"space-between" : @(YGJustifySpaceBetween),
  1008. @"space-around" : @(YGJustifySpaceAround),
  1009. @"space-evenly" : @(YGJustifySpaceEvenly)
  1010. }),
  1011. YGJustifyFlexStart,
  1012. intValue)
  1013. RCT_ENUM_CONVERTER(
  1014. YGAlign,
  1015. (@{
  1016. @"flex-start" : @(YGAlignFlexStart),
  1017. @"flex-end" : @(YGAlignFlexEnd),
  1018. @"center" : @(YGAlignCenter),
  1019. @"auto" : @(YGAlignAuto),
  1020. @"stretch" : @(YGAlignStretch),
  1021. @"baseline" : @(YGAlignBaseline),
  1022. @"space-between" : @(YGAlignSpaceBetween),
  1023. @"space-around" : @(YGAlignSpaceAround)
  1024. }),
  1025. YGAlignFlexStart,
  1026. intValue)
  1027. RCT_ENUM_CONVERTER(
  1028. YGDirection,
  1029. (@{
  1030. @"inherit" : @(YGDirectionInherit),
  1031. @"ltr" : @(YGDirectionLTR),
  1032. @"rtl" : @(YGDirectionRTL),
  1033. }),
  1034. YGDirectionInherit,
  1035. intValue)
  1036. RCT_ENUM_CONVERTER(
  1037. YGPositionType,
  1038. (@{@"absolute" : @(YGPositionTypeAbsolute), @"relative" : @(YGPositionTypeRelative)}),
  1039. YGPositionTypeRelative,
  1040. intValue)
  1041. RCT_ENUM_CONVERTER(
  1042. YGWrap,
  1043. (@{@"wrap" : @(YGWrapWrap), @"nowrap" : @(YGWrapNoWrap), @"wrap-reverse" : @(YGWrapWrapReverse)}),
  1044. YGWrapNoWrap,
  1045. intValue)
  1046. RCT_ENUM_CONVERTER(
  1047. RCTPointerEvents,
  1048. (@{
  1049. @"none" : @(RCTPointerEventsNone),
  1050. @"box-only" : @(RCTPointerEventsBoxOnly),
  1051. @"box-none" : @(RCTPointerEventsBoxNone),
  1052. @"auto" : @(RCTPointerEventsUnspecified)
  1053. }),
  1054. RCTPointerEventsUnspecified,
  1055. integerValue)
  1056. RCT_ENUM_CONVERTER(
  1057. RCTAnimationType,
  1058. (@{
  1059. @"spring" : @(RCTAnimationTypeSpring),
  1060. @"linear" : @(RCTAnimationTypeLinear),
  1061. @"easeIn" : @(RCTAnimationTypeEaseIn),
  1062. @"easeOut" : @(RCTAnimationTypeEaseOut),
  1063. @"easeInEaseOut" : @(RCTAnimationTypeEaseInEaseOut),
  1064. @"keyboard" : @(RCTAnimationTypeKeyboard),
  1065. }),
  1066. RCTAnimationTypeEaseInEaseOut,
  1067. integerValue)
  1068. @end
  1069. @interface RCTImageSource (Packager)
  1070. @property (nonatomic, assign) BOOL packagerAsset;
  1071. @end
  1072. @implementation RCTConvert (Deprecated)
  1073. /* This method is only used when loading images synchronously, e.g. for tabbar icons */
  1074. + (UIImage *)UIImage:(id)json
  1075. {
  1076. if (!json) {
  1077. return nil;
  1078. }
  1079. RCTImageSource *imageSource = [self RCTImageSource:json];
  1080. if (!imageSource) {
  1081. return nil;
  1082. }
  1083. __block UIImage *image;
  1084. if (!RCTIsMainQueue()) {
  1085. // It seems that none of the UIImage loading methods can be guaranteed
  1086. // thread safe, so we'll pick the lesser of two evils here and block rather
  1087. // than run the risk of crashing
  1088. RCTLogWarn(@"Calling [RCTConvert UIImage:] on a background thread is not recommended");
  1089. RCTUnsafeExecuteOnMainQueueSync(^{
  1090. image = [self UIImage:json];
  1091. });
  1092. return image;
  1093. }
  1094. NSURL *URL = imageSource.request.URL;
  1095. NSString *scheme = URL.scheme.lowercaseString;
  1096. if ([scheme isEqualToString:@"file"]) {
  1097. image = RCTImageFromLocalAssetURL(URL);
  1098. // There is a case where this may fail when the image is at the bundle location.
  1099. // RCTImageFromLocalAssetURL only checks for the image in the same location as the jsbundle
  1100. // Hence, if the bundle is CodePush-ed, it will not be able to find the image.
  1101. // This check is added here instead of being inside RCTImageFromLocalAssetURL, since
  1102. // we don't want breaking changes to RCTImageFromLocalAssetURL, which is called in a lot of places
  1103. // This is a deprecated method, and hence has the least impact on existing code. Basically,
  1104. // instead of crashing the app, it tries one more location for the image.
  1105. if (!image) {
  1106. image = RCTImageFromLocalBundleAssetURL(URL);
  1107. }
  1108. if (!image) {
  1109. RCTLogConvertError(json, @"an image. File not found.");
  1110. }
  1111. } else if ([scheme isEqualToString:@"data"]) {
  1112. image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
  1113. } else if ([scheme isEqualToString:@"http"] && imageSource.packagerAsset) {
  1114. image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
  1115. } else {
  1116. RCTLogConvertError(json, @"an image. Only local files or data URIs are supported.");
  1117. return nil;
  1118. }
  1119. CGFloat scale = imageSource.scale;
  1120. if (!scale && imageSource.size.width) {
  1121. // If no scale provided, set scale to image width / source width
  1122. scale = CGImageGetWidth(image.CGImage) / imageSource.size.width;
  1123. }
  1124. if (scale) {
  1125. image = [UIImage imageWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
  1126. }
  1127. if (!CGSizeEqualToSize(imageSource.size, CGSizeZero) && !CGSizeEqualToSize(imageSource.size, image.size)) {
  1128. RCTLogError(
  1129. @"Image source %@ size %@ does not match loaded image size %@.",
  1130. URL.path.lastPathComponent,
  1131. NSStringFromCGSize(imageSource.size),
  1132. NSStringFromCGSize(image.size));
  1133. }
  1134. return image;
  1135. }
  1136. + (CGImageRef)CGImage:(id)json
  1137. {
  1138. return [self UIImage:json].CGImage;
  1139. }
  1140. @end