AutoFill everywhere
Swift & UI 入门 16m

AutoFill 无处不在

AutoFill everywhere

2020年6月25日

在 Apple 官方观看视频

一句话判断

iOS 14 把 AutoFill 从密码扩展到了所有类型的表单字段——姓名、邮箱、电话、信用卡、地址,甚至一次性验证码(OTP)。如果你的 App 有注册或支付流程,正确配置 AutoFill 能把用户填表时间缩短一半。

这场 Session 讲了什么

AutoFill 在 iOS 中已经存在多年,主要功能是自动填充保存在 iCloud Keychain 或第三方密码管理器中的账号密码。iOS 14 大幅扩展了 AutoFill 的覆盖范围。

Session 介绍了几个关键更新。第一,textContentType 属性新增了更多类型,包括 .oneTimeCode(一次性验证码)、.newPassword(新密码)、.URL 等。第二,键盘上方现在会自动建议验证码(SMS OTP),用户不需要切换 App 查看短信。第三,Security Framework 的 ASPasswordCredentialIdentityASPasswordCredential 让第三方密码管理器(如 1Password)可以更好地与 AutoFill 集成。第四,Safari 中的 Web 表单也支持了更丰富的 AutoFill 类型。

值得深挖的点

SMS 验证码自动填充

这是 iOS 14 中最实用的 AutoFill 新功能。当用户收到一条格式正确的 SMS 验证码消息时(如”Your code is 123456”),键盘上方会自动显示这个验证码,用户点击即可填入。要触发这个功能,你的 UITextField 的 textContentType 需要设置为 .oneTimeCode,消息格式需要包含纯数字的验证码。

新密码建议

iOS 14 可以在用户注册新账号时自动生成强密码。将 textContentType 设置为 .newPassword,系统会在键盘上方显示一个”使用强密码”的建议。用户选择后,密码会自动保存到 iCloud Keychain。下次登录时,AutoFill 会自动填入保存的密码。

代码片段

配置注册表单的 AutoFill

场景:为注册页面配置全面的 AutoFill 支持。

import UIKit

class RegistrationViewController: UIViewController {
    
    @IBOutlet weak var nameField: UITextField!
    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var phoneField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var confirmPasswordField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureAutoFill()
    }
    
    private func configureAutoFill() {
        // 姓名:支持从通讯录自动填充
        nameField.textContentType = .name
        nameField.autocorrectionType = .no
        
        // 邮箱:支持从账户信息自动填充
        emailField.textContentType = .emailAddress
        emailField.keyboardType = .emailAddress
        emailField.autocapitalizationType = .none
        
        // 电话号码
        phoneField.textContentType = .telephoneNumber
        phoneField.keyboardType = .phonePad
        
        // 新密码:触发系统强密码建议
        passwordField.textContentType = .newPassword
        passwordField.isSecureTextEntry = true
        
        // 确认密码
        confirmPasswordField.textContentType = .newPassword
        confirmPasswordField.isSecureTextEntry = true
    }
}

配置验证码自动填充

场景:登录时自动填充 SMS 验证码。

class OTPVerificationViewController: UIViewController {
    
    @IBOutlet weak var codeField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureOTPField()
    }
    
    private func configureOTPField() {
        // 关键:设置 textContentType 为 oneTimeCode
        codeField.textContentType = .oneTimeCode
        codeField.keyboardType = .numberPad
        
        // 监听填充事件
        codeField.addTarget(self,
                            action: #selector(codeDidChange),
                            for: .editingChanged)
    }
    
    @objc private func codeDidChange() {
        guard let code = codeField.text, code.count == 6 else { return }
        
        // 验证码自动填充完成,自动提交
        verifyCode(code)
    }
    
    private func verifyCode(_ code: String) {
        // 向服务器验证
        AuthService.verifyOTP(code: code) { success in
            if success {
                self.navigateToHome()
            }
        }
    }
}

配置支付表单的 AutoFill

场景:为支付页面配置信用卡和地址自动填充。

class PaymentViewController: UIViewController {
    
    @IBOutlet weak var cardNumberField: UITextField!
    @IBOutlet weak var expiryField: UITextField!
    @IBOutlet weak var cvvField: UITextField!
    @IBOutlet weak var cardholderField: UITextField!
    @IBOutlet weak var addressField: UITextField!
    @IBOutlet weak var cityField: UITextField!
    @IBOutlet weak var zipField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configurePaymentAutoFill()
    }
    
    private func configurePaymentAutoFill() {
        // 持卡人姓名
        cardholderField.textContentType = .name
        
        // 信用卡号
        cardNumberField.textContentType = .creditCardNumber
        cardNumberField.keyboardType = .numberPad
        
        // 有效期(系统不提供自动填充,但可以手动输入)
        expiryField.textContentType = .none
        
        // CVV(安全码,不应该被自动填充)
        cvvField.textContentType = .none
        cvvField.isSecureTextEntry = true
        
        // 地址信息
        addressField.textContentType = .fullStreetAddress
        cityField.textContentType = .addressCity
        zipField.textContentType = .postalCode
    }
}

为自定义密码管理器提供 AutoFill 支持

场景:通过 AuthenticationServices 框架让你的密码管理器参与 AutoFill。

import AuthenticationServices

// 在你的 App Extension 中提供密码凭据
class CredentialProviderViewController: ASCredentialProviderViewController {
    
    // 提供凭据列表
    override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
        // 根据服务标识符查找保存的密码
        let credentials = PasswordStore.shared.findCredentials(
            for: serviceIdentifiers.first?.identifier ?? ""
        )
        
        // 显示凭据选择界面
        displayCredentials(credentials)
    }
    
    // 用户选择了凭据后
    override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
        // 查找对应的密码
        let credential = PasswordStore.shared.getCredential(
            for: credentialIdentity.recordIdentifier
        )
        
        // 返回给系统
        let passwordCredential = ASPasswordCredential(
            user: credential.username,
            password: credential.password
        )
        self.extensionContext.completeRequest(withSelectedCredential: passwordCredential)
    }
}

最佳实践

已有项目:审查所有表单页面,为每个输入字段设置正确的 textContentType。最优先处理的是验证码字段(.oneTimeCode)和注册页面的密码字段(.newPassword)。这两个改动对用户体验的提升最明显。

新项目:从第一天就为所有表单字段设置 textContentType。这是零成本的优化——几行代码就能让用户享受系统级的自动填充体验。对于支付和注册等关键转化流程,确保 AutoFill 配置正确。

还有什么值得关注

  • HTML 表单的 AutoFill 通过 autocomplete 属性控制,不需要修改 iOS 代码。
  • iOS 14 的 Safari 支持在 iframe 中使用 AutoFill,之前版本不支持。
  • 密码自动填充的安全性保证:App 只能访问自己域名下的密码,不能跨域。
WWDC 2020