From 25793eab56217a9961620761ea65ec2fcb97dcb0 Mon Sep 17 00:00:00 2001 From: Tom Underhill Date: Mon, 20 Apr 2020 20:20:02 -0700 Subject: [PATCH] Allow iOS PlatformColor strings to be ObjC or Swift UIColor selectors (#28703) Summary: Per discussion in https://github.com/react-native-community/releases/issues/186 the iOS `PlatformColor()` function is documented to use the semantic color names provided by the system. The referenced HIG documentation itself links to the `UIColor` documentation for semantic colors names. However, these names differ depending on if you are viewing the new Swift API docs or the Objective C docs. The current Objective C implementation in react-native assumes Objective C UIColor selector names that are suffixed 'Color'. But in Swift, Apple provides a Swift Extension on UIColor that makes aliases without the the 'Color' suffix and then makes the original selectors invalid presumably via `NS_UNAVAILABLE_SWIFT`. Since both selector names are valid depending on if you are using Objective C or Swift, let's make both forms be legal for `PlatformColor()`. In `RCTConvert.m` there is a dictionary of legal selector names. The code already supports the ability to have names be aliases of other selectors via a RCTSelector metadata key. The change adds code to the initialization of the map: it iterates over the keys in the map, which are all ObjC style UIColor selectors, and creates aliases by duplicating the entries, creating key names by stripping off the ObjC "Color" suffix, adds the RCTSelector key referring to the original and then appends these new Swift aliases to the map. ## Changelog [iOS] [Changed] - Allow iOS PlatformColor strings to be ObjC or Swift UIColor selectors Pull Request resolved: https://github.com/facebook/react-native/pull/28703 Test Plan: The PlatformColorExample.js is updated to use the new, shorter Swift selector names. There are still other examples in the same file and in unit tests that exercise the ObjC selector names. PlatformColor Reviewed By: shergin Differential Revision: D21147404 Pulled By: TheSavior fbshipit-source-id: 0273ec855e426b3a7ba97a87645859e05bcd4126 --- .../PlatformColor/PlatformColorExample.js | 100 +++++++++--------- React/Base/RCTConvert.m | 21 +++- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/RNTester/js/examples/PlatformColor/PlatformColorExample.js b/RNTester/js/examples/PlatformColor/PlatformColorExample.js index d0b04e21930087..5cb831c0627d06 100644 --- a/RNTester/js/examples/PlatformColor/PlatformColorExample.js +++ b/RNTester/js/examples/PlatformColor/PlatformColorExample.js @@ -22,94 +22,94 @@ function PlatformColorsExample() { colors = [ // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors // Label Colors - {label: 'labelColor', color: PlatformColor('labelColor')}, + {label: 'label', color: PlatformColor('label')}, { - label: 'secondaryLabelColor', - color: PlatformColor('secondaryLabelColor'), + label: 'secondaryLabel', + color: PlatformColor('secondaryLabel'), }, { - label: 'tertiaryLabelColor', - color: PlatformColor('tertiaryLabelColor'), + label: 'tertiaryLabel', + color: PlatformColor('tertiaryLabel'), }, { - label: 'quaternaryLabelColor', - color: PlatformColor('quaternaryLabelColor'), + label: 'quaternaryLabel', + color: PlatformColor('quaternaryLabel'), }, // Fill Colors - {label: 'systemFillColor', color: PlatformColor('systemFillColor')}, + {label: 'systemFill', color: PlatformColor('systemFill')}, { - label: 'secondarySystemFillColor', - color: PlatformColor('secondarySystemFillColor'), + label: 'secondarySystemFill', + color: PlatformColor('secondarySystemFill'), }, { - label: 'tertiarySystemFillColor', - color: PlatformColor('tertiarySystemFillColor'), + label: 'tertiarySystemFill', + color: PlatformColor('tertiarySystemFill'), }, { - label: 'quaternarySystemFillColor', - color: PlatformColor('quaternarySystemFillColor'), + label: 'quaternarySystemFill', + color: PlatformColor('quaternarySystemFill'), }, // Text Colors { - label: 'placeholderTextColor', - color: PlatformColor('placeholderTextColor'), + label: 'placeholderText', + color: PlatformColor('placeholderText'), }, // Standard Content Background Colors { - label: 'systemBackgroundColor', - color: PlatformColor('systemBackgroundColor'), + label: 'systemBackground', + color: PlatformColor('systemBackground'), }, { - label: 'secondarySystemBackgroundColor', - color: PlatformColor('secondarySystemBackgroundColor'), + label: 'secondarySystemBackground', + color: PlatformColor('secondarySystemBackground'), }, { - label: 'tertiarySystemBackgroundColor', - color: PlatformColor('tertiarySystemBackgroundColor'), + label: 'tertiarySystemBackground', + color: PlatformColor('tertiarySystemBackground'), }, // Grouped Content Background Colors { - label: 'systemGroupedBackgroundColor', - color: PlatformColor('systemGroupedBackgroundColor'), + label: 'systemGroupedBackground', + color: PlatformColor('systemGroupedBackground'), }, { - label: 'secondarySystemGroupedBackgroundColor', - color: PlatformColor('secondarySystemGroupedBackgroundColor'), + label: 'secondarySystemGroupedBackground', + color: PlatformColor('secondarySystemGroupedBackground'), }, { - label: 'tertiarySystemGroupedBackgroundColor', - color: PlatformColor('tertiarySystemGroupedBackgroundColor'), + label: 'tertiarySystemGroupedBackground', + color: PlatformColor('tertiarySystemGroupedBackground'), }, // Separator Colors - {label: 'separatorColor', color: PlatformColor('separatorColor')}, + {label: 'separator', color: PlatformColor('separator')}, { - label: 'opaqueSeparatorColor', - color: PlatformColor('opaqueSeparatorColor'), + label: 'opaqueSeparator', + color: PlatformColor('opaqueSeparator'), }, // Link Color - {label: 'linkColor', color: PlatformColor('linkColor')}, + {label: 'link', color: PlatformColor('link')}, // Nonadaptable Colors - {label: 'darkTextColor', color: PlatformColor('darkTextColor')}, - {label: 'lightTextColor', color: PlatformColor('lightTextColor')}, + {label: 'darkText', color: PlatformColor('darkText')}, + {label: 'lightText', color: PlatformColor('lightText')}, // https://developer.apple.com/documentation/uikit/uicolor/standard_colors // Adaptable Colors - {label: 'systemBlueColor', color: PlatformColor('systemBlueColor')}, - {label: 'systemBrownColor', color: PlatformColor('systemBrownColor')}, - {label: 'systemGreenColor', color: PlatformColor('systemGreenColor')}, - {label: 'systemIndigoColor', color: PlatformColor('systemIndigoColor')}, - {label: 'systemOrangeColor', color: PlatformColor('systemOrangeColor')}, - {label: 'systemPinkColor', color: PlatformColor('systemPinkColor')}, - {label: 'systemPurpleColor', color: PlatformColor('systemPurpleColor')}, - {label: 'systemRedColor', color: PlatformColor('systemRedColor')}, - {label: 'systemTealColor', color: PlatformColor('systemTealColor')}, - {label: 'systemYellowColor', color: PlatformColor('systemYellowColor')}, + {label: 'systemBlue', color: PlatformColor('systemBlue')}, + {label: 'systemBrown', color: PlatformColor('systemBrown')}, + {label: 'systemGreen', color: PlatformColor('systemGreen')}, + {label: 'systemIndigo', color: PlatformColor('systemIndigo')}, + {label: 'systemOrange', color: PlatformColor('systemOrange')}, + {label: 'systemPink', color: PlatformColor('systemPink')}, + {label: 'systemPurple', color: PlatformColor('systemPurple')}, + {label: 'systemRed', color: PlatformColor('systemRed')}, + {label: 'systemTeal', color: PlatformColor('systemTeal')}, + {label: 'systemYellow', color: PlatformColor('systemYellow')}, // Adaptable Gray Colors - {label: 'systemGrayColor', color: PlatformColor('systemGrayColor')}, - {label: 'systemGray2Color', color: PlatformColor('systemGray2Color')}, - {label: 'systemGray3Color', color: PlatformColor('systemGray3Color')}, - {label: 'systemGray4Color', color: PlatformColor('systemGray4Color')}, - {label: 'systemGray5Color', color: PlatformColor('systemGray5Color')}, - {label: 'systemGray6Color', color: PlatformColor('systemGray6Color')}, + {label: 'systemGray', color: PlatformColor('systemGray')}, + {label: 'systemGray2', color: PlatformColor('systemGray2')}, + {label: 'systemGray3', color: PlatformColor('systemGray3')}, + {label: 'systemGray4', color: PlatformColor('systemGray4')}, + {label: 'systemGray5', color: PlatformColor('systemGray5')}, + {label: 'systemGray6', color: PlatformColor('systemGray6')}, ]; } else if (Platform.OS === 'android') { colors = [ diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 0102ecf906a9a0..6de135bd1e5cb5 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -604,7 +604,7 @@ +(type)type : (id)json \ { static NSDictionary *colorMap = nil; if (colorMap == nil) { - colorMap = @{ + NSMutableDictionary *map = [@{ // https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors // Label Colors @"labelColor" : @{ @@ -729,7 +729,22 @@ +(type)type : (id)json \ // 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 *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"}, @@ -743,9 +758,11 @@ +(type)type : (id)json \ RCTIndex : @1, RCTFallback : @"controlAlternatingRowBackgroundColors" }, + }]; #endif - }; + colorMap = [map copy]; } + return colorMap; }