Skip to content

Commit

Permalink
feat: Support iOS 17 text content types (#38354)
Browse files Browse the repository at this point in the history
Summary:
While setting up a credit card form in RN, I discovered that iOS 17 supports a number of new content types ([`UITextContentType` docs](https://developer.apple.com/documentation/uikit/uitextcontenttype?language=objc)). In the docs these are marked with the `Beta` flag.

Setting up the new content types is relatively straightforward, but a change is required in https://github.com/facebook/react-native-deprecated-modules to update the `TextInput` prop types. ~~I will open a PR in that repo shortly.~~ I have [opened a PR](facebook/react-native-deprecated-modules#23) to update the prop types. ~~Once that PR is merged, a version bump for that dependency will need to be added to this PR.~~ The PR is merged and the dependency in this PR has been updated.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[IOS] [ADDED] - Added support for iOS 17+ text content types

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #38354

Test Plan: The `rn-tester` app builds and runs successfully. I have added a few new examples of inputs using the new text content types.

Reviewed By: javache

Differential Revision: D47554161

Pulled By: philIip

fbshipit-source-id: 8d4414dc6229063f81164f2d8727921c8294c92e
  • Loading branch information
robwalkerco authored and facebook-github-bot committed Jul 21, 2023
1 parent 0527aea commit 216865c
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,6 @@ export interface TextInputIOSProps {
* Give the keyboard and the system information about the expected
* semantic meaning for the content that users enter.
*
* For iOS 11+ you can set `textContentType` to `username` or `password` to
* enable autofill of login details from the device keychain.
*
* For iOS 12+ `newPassword` can be used to indicate a new password input the
* user may want to save in the keychain, and `oneTimeCode` can be used to indicate
* that a field can be autofilled by a code arriving in an SMS.
*
* To disable autofill, set textContentType to `none`.
*
* Possible values for `textContentType` are:
Expand All @@ -223,6 +216,15 @@ export interface TextInputIOSProps {
* - `'addressState'`
* - `'countryName'`
* - `'creditCardNumber'`
* - `'creditCardExpiration'` (iOS 17+)
* - `'creditCardExpirationMonth'` (iOS 17+)
* - `'creditCardExpirationYear'` (iOS 17+)
* - `'creditCardSecurityCode'` (iOS 17+)
* - `'creditCardType'` (iOS 17+)
* - `'creditCardName'` (iOS 17+)
* - `'creditCardGivenName'` (iOS 17+)
* - `'creditCardMiddleName'` (iOS 17+)
* - `'creditCardFamilyName'` (iOS 17+)
* - `'emailAddress'`
* - `'familyName'`
* - `'fullStreetAddress'`
Expand All @@ -244,6 +246,10 @@ export interface TextInputIOSProps {
* - `'password'`
* - `'newPassword'`
* - `'oneTimeCode'`
* - `'birthdate'` (iOS 17+)
* - `'birthdateDay'` (iOS 17+)
* - `'birthdateMonth'` (iOS 17+)
* - `'birthdateYear'` (iOS 17+)
*
*/
textContentType?:
Expand All @@ -254,6 +260,15 @@ export interface TextInputIOSProps {
| 'addressState'
| 'countryName'
| 'creditCardNumber'
| 'creditCardExpiration'
| 'creditCardExpirationMonth'
| 'creditCardExpirationYear'
| 'creditCardSecurityCode'
| 'creditCardType'
| 'creditCardName'
| 'creditCardGivenName'
| 'creditCardMiddleName'
| 'creditCardFamilyName'
| 'emailAddress'
| 'familyName'
| 'fullStreetAddress'
Expand All @@ -275,6 +290,10 @@ export interface TextInputIOSProps {
| 'password'
| 'newPassword'
| 'oneTimeCode'
| 'birthdate'
| 'birthdateDay'
| 'birthdateMonth'
| 'birthdateYear'
| undefined;

/**
Expand Down Expand Up @@ -569,6 +588,11 @@ export interface TextInputProps
| 'cc-exp-month'
| 'cc-exp-year'
| 'cc-number'
| 'cc-name'
| 'cc-given-name'
| 'cc-middle-name'
| 'cc-family-name'
| 'cc-type'
| 'country'
| 'current-password'
| 'email'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ export type TextContentType =
| 'addressState'
| 'countryName'
| 'creditCardNumber'
| 'creditCardExpiration'
| 'creditCardExpirationMonth'
| 'creditCardExpirationYear'
| 'creditCardSecurityCode'
| 'creditCardType'
| 'creditCardName'
| 'creditCardGivenName'
| 'creditCardMiddleName'
| 'creditCardFamilyName'
| 'emailAddress'
| 'familyName'
| 'fullStreetAddress'
Expand All @@ -182,7 +191,11 @@ export type TextContentType =
| 'username'
| 'password'
| 'newPassword'
| 'oneTimeCode';
| 'oneTimeCode'
| 'birthdate'
| 'birthdateDay'
| 'birthdateMonth'
| 'birthdateYear';

export type enterKeyHintType =
| 'enter'
Expand Down Expand Up @@ -418,7 +431,16 @@ export type Props = $ReadOnly<{|
* - `additional-name`
* - `address-line1`
* - `address-line2`
* - `birthdate-day` (iOS 17+)
* - `birthdate-full` (iOS 17+)
* - `birthdate-month` (iOS 17+)
* - `birthdate-year` (iOS 17+)
* - `cc-number`
* - `cc-csc` (iOS 17+)
* - `cc-exp` (iOS 17+)
* - `cc-exp-day` (iOS 17+)
* - `cc-exp-month` (iOS 17+)
* - `cc-exp-year` (iOS 17+)
* - `country`
* - `current-password`
* - `email`
Expand All @@ -437,22 +459,18 @@ export type Props = $ReadOnly<{|
*
* The following values work on iOS only:
*
* - `cc-name` (iOS 17+)
* - `cc-given-name` (iOS 17+)
* - `cc-middle-name` (iOS 17+)
* - `cc-family-name` (iOS 17+)
* - `cc-type` (iOS 17+)
* - `nickname`
* - `organization`
* - `organization-title`
* - `url`
*
* The following values work on Android only:
*
* - `birthdate-day`
* - `birthdate-full`
* - `birthdate-month`
* - `birthdate-year`
* - `cc-csc`
* - `cc-exp`
* - `cc-exp-day`
* - `cc-exp-month`
* - `cc-exp-year`
* - `gender`
* - `name-family`
* - `name-given`
Expand Down Expand Up @@ -488,6 +506,11 @@ export type Props = $ReadOnly<{|
| 'cc-exp-month'
| 'cc-exp-year'
| 'cc-number'
| 'cc-name'
| 'cc-given-name'
| 'cc-middle-name'
| 'cc-family-name'
| 'cc-type'
| 'country'
| 'current-password'
| 'email'
Expand Down
56 changes: 46 additions & 10 deletions packages/react-native/Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,15 @@ export type TextContentType =
| 'addressState'
| 'countryName'
| 'creditCardNumber'
| 'creditCardExpiration'
| 'creditCardExpirationMonth'
| 'creditCardExpirationYear'
| 'creditCardSecurityCode'
| 'creditCardType'
| 'creditCardName'
| 'creditCardGivenName'
| 'creditCardMiddleName'
| 'creditCardFamilyName'
| 'emailAddress'
| 'familyName'
| 'fullStreetAddress'
Expand All @@ -220,7 +229,11 @@ export type TextContentType =
| 'username'
| 'password'
| 'newPassword'
| 'oneTimeCode';
| 'oneTimeCode'
| 'birthdate'
| 'birthdateDay'
| 'birthdateMonth'
| 'birthdateYear';

export type enterKeyHintType =
// Cross Platform
Expand Down Expand Up @@ -462,7 +475,16 @@ export type Props = $ReadOnly<{|
* - `additional-name`
* - `address-line1`
* - `address-line2`
* - `birthdate-day` (iOS 17+)
* - `birthdate-full` (iOS 17+)
* - `birthdate-month` (iOS 17+)
* - `birthdate-year` (iOS 17+)
* - `cc-number`
* - `cc-csc` (iOS 17+)
* - `cc-exp` (iOS 17+)
* - `cc-exp-day` (iOS 17+)
* - `cc-exp-month` (iOS 17+)
* - `cc-exp-year` (iOS 17+)
* - `country`
* - `current-password`
* - `email`
Expand All @@ -481,22 +503,18 @@ export type Props = $ReadOnly<{|
*
* The following values work on iOS only:
*
* - `cc-name` (iOS 17+)
* - `cc-given-name` (iOS 17+)
* - `cc-middle-name` (iOS 17+)
* - `cc-family-name` (iOS 17+)
* - `cc-type` (iOS 17+)
* - `nickname`
* - `organization`
* - `organization-title`
* - `url`
*
* The following values work on Android only:
*
* - `birthdate-day`
* - `birthdate-full`
* - `birthdate-month`
* - `birthdate-year`
* - `cc-csc`
* - `cc-exp`
* - `cc-exp-day`
* - `cc-exp-month`
* - `cc-exp-year`
* - `gender`
* - `name-family`
* - `name-given`
Expand Down Expand Up @@ -532,6 +550,11 @@ export type Props = $ReadOnly<{|
| 'cc-exp-month'
| 'cc-exp-year'
| 'cc-number'
| 'cc-name'
| 'cc-given-name'
| 'cc-middle-name'
| 'cc-family-name'
| 'cc-type'
| 'country'
| 'current-password'
| 'email'
Expand Down Expand Up @@ -1572,7 +1595,20 @@ const autoCompleteWebToAutoCompleteAndroidMap = {
const autoCompleteWebToTextContentTypeMap = {
'address-line1': 'streetAddressLine1',
'address-line2': 'streetAddressLine2',
bday: 'birthdate',
'bday-day': 'birthdateDay',
'bday-month': 'birthdateMonth',
'bday-year': 'birthdateYear',
'cc-csc': 'creditCardSecurityCode',
'cc-exp-month': 'creditCardExpirationMonth',
'cc-exp-year': 'creditCardExpirationYear',
'cc-exp': 'creditCardExpiration',
'cc-given-name': 'creditCardGivenName',
'cc-additional-name': 'creditCardMiddleName',
'cc-family-name': 'creditCardFamilyName',
'cc-name': 'creditCardName',
'cc-number': 'creditCardNumber',
'cc-type': 'creditCardType',
'current-password': 'password',
country: 'countryName',
email: 'emailAddress',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ - (void)setTextContentType:(NSString *)type
static NSDictionary<NSString *, NSString *> *contentTypeMap;

dispatch_once(&onceToken, ^{
contentTypeMap = @{
NSDictionary<NSString *, NSString *> *mutableContentTypeMap = @{
@"none" : @"",
@"URL" : UITextContentTypeURL,
@"addressCity" : UITextContentTypeAddressCity,
Expand Down Expand Up @@ -265,6 +265,28 @@ - (void)setTextContentType:(NSString *)type
@"newPassword" : UITextContentTypeNewPassword,
@"oneTimeCode" : UITextContentTypeOneTimeCode,
};

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */
if (@available(iOS 17.0, *)) {
mutableContentTypeMap = [[mutableContentTypeMap mutableCopy] addEntriesFromDictionary:@{
@"creditCardExpiration" : UITextContentTypeCreditCardExpiration,
@"creditCardExpirationMonth" : UITextContentTypeCreditCardExpirationMonth,
@"creditCardExpirationYear" : UITextContentTypeCreditCardExpirationYear,
@"creditCardSecurityCode" : UITextContentTypeCreditCardSecurityCode,
@"creditCardType" : UITextContentTypeCreditCardType,
@"creditCardName" : UITextContentTypeCreditCardName,
@"creditCardGivenName" : UITextContentTypeCreditCardGivenName,
@"creditCardMiddleName" : UITextContentTypeCreditCardMiddleName,
@"creditCardFamilyName" : UITextContentTypeCreditCardFamilyName,
@"birthdate" : UITextContentTypeBirthdate,
@"birthdateDay" : UITextContentTypeBirthdateDay,
@"birthdateMonth" : UITextContentTypeBirthdateMonth,
@"birthdateYear" : UITextContentTypeBirthdateYear,
}];
}
#endif

contentTypeMap = mutableContentTypeMap;
});

// Setting textContentType to an empty string will disable any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType)
static NSDictionary<NSString *, NSString *> *contentTypeMap;

dispatch_once(&onceToken, ^{
contentTypeMap = @{
NSDictionary<NSString *, NSString *> *mutableContentTypeMap = @{
@"" : @"",
@"none" : @"",
@"URL" : UITextContentTypeURL,
Expand Down Expand Up @@ -212,6 +212,28 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType)
@"newPassword" : UITextContentTypeNewPassword,
@"oneTimeCode" : UITextContentTypeOneTimeCode,
};

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */
if (@available(iOS 17.0, *)) {
mutableContentTypeMap = [[mutableContentTypeMap mutableCopy] addEntriesFromDictionary:@{
@"creditCardExpiration" : UITextContentTypeCreditCardExpiration,
@"creditCardExpirationMonth" : UITextContentTypeCreditCardExpirationMonth,
@"creditCardExpirationYear" : UITextContentTypeCreditCardExpirationYear,
@"creditCardSecurityCode" : UITextContentTypeCreditCardSecurityCode,
@"creditCardType" : UITextContentTypeCreditCardType,
@"creditCardName" : UITextContentTypeCreditCardName,
@"creditCardGivenName" : UITextContentTypeCreditCardGivenName,
@"creditCardMiddleName" : UITextContentTypeCreditCardMiddleName,
@"creditCardFamilyName" : UITextContentTypeCreditCardFamilyName,
@"birthdate" : UITextContentTypeBirthdate,
@"birthdateDay" : UITextContentTypeBirthdateDay,
@"birthdateMonth" : UITextContentTypeBirthdateMonth,
@"birthdateYear" : UITextContentTypeBirthdateYear,
}];
}
#endif

contentTypeMap = mutableContentTypeMap;
});

return contentTypeMap[RCTNSStringFromString(contentType)] ?: @"";
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
"base64-js": "^1.5.1",
"deprecated-react-native-prop-types": "4.1.0",
"deprecated-react-native-prop-types": "4.2.0",
"event-target-shim": "^5.0.1",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
Expand Down
15 changes: 15 additions & 0 deletions packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,12 @@ exports.examples = ([
<WithLabel label="one-time-code">
<TextInput autoComplete="one-time-code" style={styles.default} />
</WithLabel>
<WithLabel label="birthdate-full">
<TextInput autoComplete="birthdate-full" style={styles.default} />
</WithLabel>
<WithLabel label="cc-name">
<TextInput autoComplete="cc-name" style={styles.default} />
</WithLabel>
</View>
);
},
Expand All @@ -829,6 +835,15 @@ exports.examples = ([
style={styles.default}
/>
</WithLabel>
<WithLabel label="creditCardExpiration">
<TextInput
textContentType="creditCardExpiration"
style={styles.default}
/>
</WithLabel>
<WithLabel label="birthdate">
<TextInput textContentType="birthdate" style={styles.default} />
</WithLabel>
</View>
);
},
Expand Down
Loading

0 comments on commit 216865c

Please sign in to comment.