iOS 密码自动填充:ASCredentialProviderViewController 真机调试失效问题解析
2024-10-13 11:10:33
在 iOS 开发中,不少开发者在尝试使用 ASCredentialProviderViewController
实现密码自动填充功能时,会遇到一个棘手的问题:明明在模拟器或者 Catalyst 环境下运行良好,但一到真机上,点击键盘工具栏的应用名称,扩展程序却毫无反应,打不开。这到底是什么原因导致的呢?
其实,这个问题的根源通常在于 entitlements 和 info.plist 文件的配置上。这两个文件就像应用的身份证和说明书,如果信息填写错误或者缺失,系统就无法识别和调用你的扩展程序。
首先,我们来仔细检查一下 entitlements 文件。 这个文件主要用于声明应用的权限,例如访问网络、使用 iCloud 等等。对于密码自动填充功能,我们需要确保项目中包含了 Associated Domains
这个 entitlement,并且关联域名配置正确。关联域名简单来说就是你的应用可以访问的网站域名,格式类似于 webcredentials:yourdomain.com
。 ASCredentialProviderViewController
需要依靠关联域名来判断哪些网站的密码需要自动填充,如果域名配置错误,它就找不到需要填充的密码了。
接下来,我们需要确认 info.plist 文件中的设置是否正确。 info.plist 文件存储了应用的各种配置信息,包括应用名称、版本号、支持的设备等等。对于密码自动填充功能,我们需要关注 NSExtension
-> NSExtensionAttributes
-> Credential Provider Extension Keychain Access Groups
这个键值对。你需要在这里添加一个 Keychain Access Group,并且确保这个 Group 与你的主应用的 Keychain Access Group 完全一致。这是因为扩展程序需要访问主应用存储的密码信息,如果 Keychain Access Group 不一致,扩展程序就无法读取到密码了。
除了以上两点,还有一些容易被忽视的细节也需要注意:
- 扩展程序的 Bundle Identifier 必须与主应用的 Bundle Identifier 不同。 这是苹果官方的硬性规定,如果 Bundle Identifier 相同,扩展程序可能无法正常工作,甚至会导致应用审核被拒。
- 在
ASCredentialProviderViewController
的viewDidLoad
方法中,你需要主动调用extensionContext.loadCredentialIdentities(forURL:completionHandler:)
方法来加载密码信息。 这个方法会异步加载存储在 Keychain 中的密码信息,并在加载完成后调用 completionHandler,将加载结果返回给你。 - 在
ASCredentialProviderViewController
的prepareCredentialEntries(for:completionHandler:)
方法中,你需要根据加载到的密码信息创建ASCredentialEntry
对象,并将这些对象添加到credentialEntries
数组中。ASCredentialEntry
对象就像一个密码的容器,包含了密码的用户名、密码等信息,你需要将这些容器准备好,以便后续展示给用户。 - 在
ASCredentialProviderViewController
的provideCredentialIdentities(for:completionHandler:)
方法中,你需要将准备好的credentialEntries
数组传递给 completionHandler。 这样,键盘工具栏才会显示你的应用名称,用户点击后才能弹出ASCredentialProviderViewController
界面,选择需要填充的密码。
为了帮助你更好地理解,下面提供一个简单的示例代码,演示了如何使用 ASCredentialProviderViewController
实现密码自动填充功能:
import AuthenticationServices
class PasswordProviderViewController: ASCredentialProviderViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 从 Keychain 加载密码信息
extensionContext.loadCredentialIdentities(forURL: URL(string: "https://yourdomain.com")!) { (identities, error) in
if let error = error {
print("加载密码信息出错: \(error)")
return
}
// 根据加载到的密码信息创建 ASCredentialEntry 对象
var credentialEntries = [ASCredentialEntry]()
for identity in identities {
let credentialEntry = ASCredentialEntry(credentialIdentity: identity, authenticationContext: .usernameAndPassword)
credentialEntries.append(credentialEntry)
}
// 将 ASCredentialEntry 对象传递给 extensionContext
self.extensionContext.provideCredentialIdentities(credentialEntries) { (error) in
if let error = error {
print("提供密码信息出错: \(error)")
}
}
}
}
override func prepareCredentialEntries(for url: URL, completionHandler: @escaping ([ASCredentialEntry]?) -> Void) {
// 准备指定 URL 的密码信息
// ...
}
override func provideCredentialIdentities(for url: URL, completionHandler: @escaping ([ASPasswordCredentialIdentity]?, Error?) -> Void) {
// 提供指定 URL 的密码信息
// ...
}
}
需要说明的是,以上代码只是一个简单的示例,实际应用中你需要根据自己的具体需求进行修改和完善。
最后,我们来回顾一下解决 ASCredentialProviderViewController
无法正常打开问题的步骤:
- 仔细检查 entitlements 文件,确保包含
Associated Domains
entitlement,并且关联域名配置正确。 - 仔细检查 info.plist 文件,确保
Credential Provider Extension Keychain Access Groups
与主应用的 Keychain Access Group 一致。 - 确保扩展程序的 Bundle Identifier 与主应用的 Bundle Identifier 不同。
- 在
ASCredentialProviderViewController
的viewDidLoad
方法中调用extensionContext.loadCredentialIdentities(forURL:completionHandler:)
方法加载密码信息。 - 在
ASCredentialProviderViewController
的prepareCredentialEntries(for:completionHandler:)
方法中创建ASCredentialEntry
对象,并添加到credentialEntries
数组中。 - 在
ASCredentialProviderViewController
的provideCredentialIdentities(for:completionHandler:)
方法中将credentialEntries
数组传递给 completionHandler。
通过以上步骤,你应该能够解决 ASCredentialProviderViewController
无法正常打开的问题,并在你的应用中成功实现密码自动填充功能,为用户提供更加便捷的登录体验。在开发过程中,建议你仔细阅读苹果官方文档,并参考一些优秀的开源项目,这可以帮助你更好地理解和使用 ASCredentialProviderViewController
。
常见问题解答:
1. 为什么我的关联域名配置正确,但扩展程序还是无法打开?
除了关联域名配置正确外,还需要确保你的网站已经正确配置了 Apple App Site Association 文件,并且文件内容符合苹果的要求。
2. Keychain Access Group 应该如何设置?
Keychain Access Group 应该是一个字符串,用于标识一组可以共享 Keychain 数据的应用。你可以使用主应用的 Bundle Identifier 作为 Keychain Access Group,或者创建一个新的 Group,并将主应用和扩展程序都添加到这个 Group 中。
3. ASCredentialProviderViewController
支持哪些认证方式?
ASCredentialProviderViewController
目前支持用户名密码认证和通行码认证两种方式。
4. 如何在 prepareCredentialEntries(for:completionHandler:)
方法中根据不同的 URL 提供不同的密码信息?
你可以在 prepareCredentialEntries(for:completionHandler:)
方法中根据传入的 URL 参数,加载不同的密码信息,并创建相应的 ASCredentialEntry
对象。
5. 如何自定义 ASCredentialProviderViewController
的界面?
你可以通过继承 ASCredentialProviderViewController
并重写相关方法来定制界面,例如修改背景颜色、添加自定义视图等等。