Experience in Adapting iOS Password AutoFill

Wan Xiao
10 min readJan 30, 2024

--

Recently, my work shifted from Android development to iOS development. The first task was to adapt our app for Password AutoFill, allowing users to store their account passwords in iOS, and thus avoid constantly forgetting them.

To my surprise, using Password AutoFill comes with many limitations and bugs, and requires handling some unexpected scenarios. If not correctly adapted, there’s a high chance that the user’s account password will not be saved, or an incorrect account password will be saved.

Note that the term “username” used in this article refers to the username used for logging in, not the user’s nickname or real name. This will not be reiterated in the following text.

Enabling Password AutoFill in iOS

Taking iOS 17 as an example, to enable Password AutoFill, one needs to go to Settings — Passwords — Password Options and turn on AutoFill Passwords and Passkeys.

At the same time, iOS also features a function to automatically generate and fill in strong passwords. This requires going to Settings — top iCloud Account — iCloud, and ensuring that Passwords and Keychain are turned on.

With these settings, iOS gains the ability to automatically generate strong passwords and autofill account passwords.

Adapting Password AutoFill in the App

Apps must have an associated domain, which can be referenced in Apple’s documentation Supporting associated domains.

According to Apple’s documentation Enabling Password AutoFill on a text input view, it’s sufficient to set the correct UITextContentType for the corresponding UITextField to make Password AutoFill work correctly. Possible UITextContentType values include:

UITextContentType value

When UITextContentType == UITextContentTypeNewPassword, iOS will automatically generate a suggested strong password:

Strong password

If you want iOS to generate a password that meets the requirements of your app, you can use passwordRules to inform iOS of the rules for generating the password. For example, the following rule requires a minimum of 10 characters and a maximum of 14 characters for the password, and mandates the inclusion of lowercase, uppercase, and numerical characters:

minlength: 10; maxlength: 14; required: lower; required: upper; required: digit;

Rules can be generated or verified through Apple’s website: https://developer.apple.com/password-rules/

Note that iOS only suggests a strong password when the isSecureTextEntry property of the text field is set to YES.

The information introduced above is either from official iOS sources or can be found on the internet. However, in reality, during the adaptation process, combined with business code, there are various issues that need to be addressed.

General Working Mechanism of Password AutoFill

Based on Apple’s documentation and my own testing results, iOS Password AutoFill uses a heuristic method to determine when a user is logging in, creating a password, which text field is for the username, which is for the password, and which is for the new password. Password AutoFill will perform relevant operations, including saving/updating passwords, when a UIViewController with the pertinent text fields closes. In some cases, it may also prompt a UIActionSheet to ask the user whether they want to save/update the password.

Passwords saved by Password AutoFill in native iOS apps generally contain three fields: Username, Password, and Associated Domains. These can be viewed in Settings — Passwords.

Associated domains can be referenced in Apple’s documentation Supporting associated domains. This element is automatically determined by the system based on the app’s associated domain, and this domain must pass verification; otherwise, Password AutoFill will not function. Developers cannot directly specify this through the API, nor can they misuse the associated domains of other apps. When a user browses a page under this domain or subdomain in Safari and needs to enter a password, Safari will automatically prompt to fill in the password saved from the app.

Since it operates on a heuristic basis, there are inevitably occasions of incorrect judgments. Therefore, iOS also allows developers to set the UITextContentType property of UITextField to explicitly specify the specific role of the text field.

Unfortunately, based on my testing, even up to iOS 17, Password AutoFill merely considers the UITextContentType set by the developer and mostly still prioritizes heuristic methods. This leads to situations where setting the UITextContentType might be ineffective, and sometimes it’s even necessary to set an incorrect UITextContentType to make Password AutoFill function correctly.

Scenarios Where Users Need to Save/Update Passwords

Users typically need to save/update passwords in the following scenarios:

  • Setting a password for the first time after registering an account
  • Logging in on a new device for the first time
  • Changing the password
  • Changing the login account

Since Password AutoFill only saves the username and password from an app and does not have a concept like userId, when a user changes their login account and logs in again, Password AutoFill will treat it as a different account and remember the password accordingly.

Password AutoFill Does Not Know When User Login/Registration Succeeds

Unlike Google’s One Tap for Android, the app does not need to directly inform iOS of the username and password. Password AutoFill merely operates in a heuristic way; it does not know whether the user’s login/registration has been successful or not. This information is only known to the app, and Apple has not provided any interface for apps to inform the system about it.

Password AutoFill only prompts the user to save/update passwords when a UIViewController with username and password input fields is closed. For instance, if a user enters a username and password on the login page but exits without logging in, the system will still prompt to save the password. If the user entered an incorrect password, it could be saved mistakenly.

Therefore, an important aspect of adapting Password AutoFill is to ensure that when a user exits the username and password interface without going through the actual password verification process, the contents of the UITextField should be cleared to prevent Password AutoFill from prompting the user to save.

Invisible Username UITextField

Many modern apps’ login processes often start by having the user input a username, after which, if the backend identifies the user as existing, a page with only a password input field is opened.

As previously mentioned, Password AutoFill operates heuristically. However, even in iOS 17, it only functions correctly when both a username input field and a password input field are present in the UIViewController. If your page only has a password input field, Password AutoFill won’t work properly.

The solution is to add a non-interactive UITextField (userInteractionEnabled = NO) right above the password input field, and fill it with the user’s username, like the one entered by the user on the previous page. Adding an input field to display the username on a page originally lacking one would change the page design. To avoid altering the design, this non-interactive UITextField can be made invisible. Note that you should not use the hidden property to hide the UITextField, as Password AutoFill does not recognize hidden UITextFields.

UITextField *tf = UITextField.new;
tf.text = {username for login};
tf.textContentType = UITextContentTypeUsername;
tf.textColor = {same color as your background};
tf.userInteractionEnabled = NO;

However, we can make the UITextField invisible to the user but visible to the system. The implementation is quite straightforward: set the UITextField’s text color to match the background color.

Note in terms of UI layout, the UITextField holding the username should be placed directly above the password UITextField. If it’s a password change page, it should be right above the field for the new password.

Avoiding User Nicknames Being Saved as Usernames

In some apps’ registration processes, the flow might start with OTP verification of the phone number, followed by allowing the user to set a nickname and password. The UI layout might look like this:

User Nickname Input Field
User Password Input Field

In this scenario, Password AutoFill will mistakenly save the nickname field above the password field as the username.

Therefore, you need to place an invisible username UITextField above the password field and fill it with the user’s actual username to ensure Password AutoFill saves the correct username.

This means your UI layout should ultimately be arranged like this:

User Nickname Input Field
Username Input Field (invisible and non-editable)
User Password Input Field

Note that the first UITextField above the password input field must be your username input field. There should be no other UITextFields between them, as Password AutoFill always considers the first input field above the password field as the username input field, even if you have not specified a UITextContentType.

Preventing Modifications to the Invisible Username UITextField

The invisible username UITextField needs to be filled with the correct username to function properly. You also need to ensure that the UITextField where you store the username is not modified.

Although users can’t modify it, the system keyboard can. iOS’s keyboard sometimes prompts users to autofill usernames. If a user selects the system-provided option, the system will directly modify the username UITextField, leading to an incorrect username being saved. We need to revert it back to the original. The simplest way is to listen for text change events and revert the text back to the desired username if it doesn’t match what we expect:

// UITextField *tf = UITextField.new;
// self.usernameForAutoFill = {username for login}
[tf addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

- (void)textFieldDidChange:(UITextField *)textField {
if (textField == self.usernameTFForAutoFill) {
if (![textField.text isEqualToString:self.usernameForAutoFill]) {
textField.text = self.usernameForAutoFill;
}
}
}

Potential Issues When isSecureTextEntry is Set to NO

Many modern apps now have a ‘show password’ button in the password input field. When clicked, the password is displayed in plaintext:

Password Input Field with a Show Password Button

This is often achieved by modifying the isSecureTextEntry property of a UITextField.

- (void)onTap:(UIButton *)sender {
self.passwordTextField.secureTextEntry = !self.passwordTextField.isSecureTextEntry;
}

However, this feature may lead to unexpected behavior with iOS Password AutoFill. If you observe closely, you’ll notice that many password input fields within the iOS system do not support plaintext password display. iOS Password AutoFill determines whether a UITextField is a password field not only based on UITextContentType but also by checking the secureTextEntry property. It's only when Password AutoFill recognizes your password text field as an approved password field that it functions correctly.

Since I have only tested Password AutoFill on iOS 16 and iOS 17, I can provide specific behavior observed on these versions as follows:

iOS 16
If the secureEntryText property of a UITextField is set to NO when the UIViewController is closed, then Password AutoFill does not consider it as a password field.

iOS 17
If a UITextField has secureEntryText set to YES when the user interacts with it, Password AutoFill will recognize it as a password field. Even if its secureEntryText is later set to NO, Password AutoFill will still remember it as a password field.

However, in the case of creating a new password with UITextContentTypeNewPassword, at least one of the new password text fields should have secureEntryText set to YES when the UIViewController is closed for Password AutoFill to prompt saving the password.

Therefore, it’s advisable to change the “show password” button behavior to temporarily display plaintext while it’s pressed and revert to displaying ciphertext when released. This approach reduces the likelihood of issues.

It’s also a good practice to proactively set secureTextEntry to YES for all password fields before making a request to the server.

Issue of Repeated Strong Password Suggestions in iOS 16

UITextContentTypeNewPassword is used for UITextField related to new passwords. In this scenario, iOS generates suggested strong passwords when conditions are met, but users can also choose to set their own passwords by clicking ‘Choose My Own Password.’

On pages where users typically set passwords, they are often required to enter the new password twice. However, there is a strange bug in iOS 16. When a user clicks the first new password input field, selects ‘Choose My Own Password,’ enters a password, and then clicks the second new password input field, iOS 16 suggests a strong password again, resulting in a poor user experience.

There is a workaround to bypass this bug, which is to set the UITextContentType of the second new password input field to UITextContentTypePassword.

In iOS 17, your text fields should be configured like this:

UITextField (UITextContentTypeUsername)
UITextField (UITextContentTypeNewPassword)
UITextField (UITextContentTypeNewPassword)

And on iOS 16, it needs to be changed to:

UITextField (UITextContentTypeUsername)
UITextField (UITextContentTypeNewPassword)
UITextField (UITextContentTypePassword)

The heuristic working method of Password AutoFill correctly recognizes the second UITextField as a new password input field as well.

Issue with Password AutoFill Not Saving New Passwords

iOS Password AutoFill has another frustrating issue. If the last UITextField edited by the user is not the one associated with UITextContentTypeNewPassword, Password AutoFill will not save the new password.

Therefore, on pages such as registration, password modification, or password reset, just before sending a request to the server, you need to check if the last UITextField edited by the user is not associated with UITextContentTypeNewPassword. If it’s not, you should proactively make it becomeFirstResponder, let the user to click the button again to trigger the request.

You can achieve this by using the textFieldDidBeginEditing method of UITextFieldDelegate to keep track of the last UITextField edited. If it’s not associated with UITextContentTypeNewPassword, make it becomeFirstResponder.

[newPasswordTextField becomeFirstResponder];

Please be cautious not to immediately call resignFirstResponder at this point in an attempt to dismiss the raised keyboard. Otherwise, iOS will generate a new strong password.

My Perspective on iOS Password AutoFill

Initially, I hoped that users could conveniently remember their passwords through Password AutoFill. However, during the actual development process and the series of issues I encountered, I found Password AutoFill to be very unreliable, and in severe cases, it could lead to problems. Therefore, after completing support for Password AutoFill, I shifted my goal to remembering users’ passwords within the app, storing passwords in the user’s Keychain for a better user experience. This approach also ensures the security of password storage, with iOS providing protection.

--

--