I have to authenticate to my app with a web login page using WKWebView
and to simplify the user experience the Password Autofill feature is added with all required steps:
AutoFill works correctly and suggests username/password variants from iCloud Keychain in the QuickType bar. But it doesn’t show “Save password” alert as Safari does with the same login page.
How to enable or implement the saving password alert for WKWebView
?
You can trigger the saving password alert manually by adding/removing correct configured UITextField
in your view hierarchy:
func showSavePassword(view: UIView, username: String, password: String) {
let usernameInput = UITextField(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
usernameInput.textContentType = .username
usernameInput.text = username
view.addSubview(usernameInput)
let passwordInput = UITextField(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
passwordInput.textContentType = .password
passwordInput.text = password
view.addSubview(passwordInput)
passwordInput.becomeFirstResponder()
usernameInput.removeFromSuperview()
passwordInput.removeFromSuperview()
}
To make it work automatically you can implement checking for username/password parameters in httpBody
for each new request with WKNavigationDelegate
:
extension URLRequest {
// Parse parameters
var httpBodyParams: [String : String?]? {
guard let httpBody,
let text = String(data: httpBody, encoding: .utf8),
let components = URLComponents(string: "?" + text), // Add '?' to make a valid url
let queryItems = components.queryItems
else {
return nil
}
return queryItems
.map { [$0.name : $0.value] }
.reduce([:], { $0.merging($1) { _, new in new } })
}
}
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
if let params = navigationAction.request.httpBodyParams,
let username = params["username"] as? String,
let password = params["password"] as? String
{
showSavePassword(view: view, username: username, password: password)
}
return .allow
}
}