iOS クロスプラットフォームアカウントパスワード統合、ログイン体験の強化
Sign in with Apple に加えて導入すべき機能

Photo by Dan Nelson
機能
ウェブサイトとAPPの両方を提供しているサービスで最もよくある問題は、ユーザーがウェブサイトで登録・ログインしパスワードを保存しているにもかかわらず、APPをインストールして開いた際に、ログイン画面で再度一からアカウントとパスワードを入力しなければならない不便さです。この機能は、スマホに保存されているアカウント情報をウェブサイトと関連付けられたAPPに自動で入力し、ユーザーのログインプロセスを高速化します。
効果図

余計な説明は抜きにして、まず完成したスクリーンショットをお見せします。
一見するとiOS ≥ 11のPassword AutoFill機能のように見えますが、よく見るとキーボードは表示されていません。
また、「保存されたパスワードを選択」ボタンをタップして初めて、アカウントとパスワードの選択ウィンドウが表示されています。
Password AutoFillについて触れたので、まずはPassword AutoFillの紹介と設定方法を説明します!
パスワード自動入力

対応バージョン:iOS 11以上
現在はもうiOS 14で、この機能は非常に一般的で特別なものではありません。APP内のアカウントとパスワードのログイン画面でキーボードを呼び出すと、ウェブ版サービスのアカウントとパスワードを素早く選択でき、選択すると自動で入力され、すぐにログインできます!
では、APPとWebはどのようにして相互認証を行うのでしょうか?
Associated Domains!APP内でAssociated Domainsを指定し、ウェブサイトにapple-app-site-associationファイルをアップロードすると、両者が認識し合えます。
1. プロジェクト設定の「Signing & Capabilities」-> 左上の「+ Capabilities」->「Associated Domains」

webcredentials:あなたのウェブサイトドメイン を追加します(例: webcredentials:google.com)。
2. Apple開発者アカウント にログインする
「 Membership 」タブで「 Team ID 」を記録する

3.「Certificates, Identifiers & Profiles」→「Identifiers」→あなたのプロジェクトを選択→「Associated Domains」機能を有効にする

APP側の設定が完了しました!
4.Webサイト側の設定
「 apple-app-site-association 」という名前のファイル(拡張子なし)を作成し、テキストエディタで開いて以下の内容を入力してください:
{
"webcredentials": {
"apps": [
"TeamID.BundleId"
]
}
}
TeamID.BundleId をあなたのプロジェクト設定に置き換えてください(例:TeamID = ABCD 、BundleID = li.zhgchg.demoapp => ABCD.li.zhgchg.demoapp)
このファイルをウェブサイトの ルートディレクトリ または /.well-known ディレクトリにアップロードしてください。例えば、あなたの webcredentials のサイトドメイン が google.com に設定されている場合、このファイルは google.com/apple-app-site-association または google.com/.well-known/apple-app-site-association でアクセス可能である必要があります。
補足:サブドメイン

公式ドキュメントからの抜粋ですが、サブドメインの場合はすべてAssociated Domainsに記載する必要があります。
Web側の設定が完了しました!
補足:applinks
ここで発見したのは、universal link の applinks を設定していれば、webcredentials を追加しなくても機能する場合があるということです。しかし、ドキュメント通りに設定しておきましょう。将来的に問題が起きないとは限りません。
プログラムに戻る
Code 部分、TextField を以下のように設定するだけで十分です:
usernameTextField.textContentType = .username // ユーザー名の自動入力を有効にする
passwordTextField.textContentType = .password // パスワードの自動入力を有効にする
新規登録の場合、パスワード確認欄には以下を使用できます:
repeatPasswordTextField.textContentType = .newPassword
// 新しいパスワードの入力フィールドとして設定
この時点で再度ビルド&実行すると、アカウント入力時にキーボード上部に同じサイトで保存されているパスワードの候補が表示されます。
完了!

表示されない?
おそらく自動入力パスワード機能がオンになっていないためです(シミュレーターのデフォルトはオフです)。「設定」->「パスワード」->「自動入力パスワード」->「自動入力パスワードをオン」にしてください。

あるいは、そのサイトに既存のパスワードがない場合でも、「設定」->「パスワード」-> 右上の「+ 新規追加」-> で追加できます。

本題に入る
前菜のPassword AutoFillの紹介が終わったら、次は本題に入ります;効果図のような効果をどうやって実現するか。
Shared Web Credentials
iOS 8.0から始まりましたが、以前はあまりAPPで使われていませんでした。Password AutoFillが登場する前から、このAPIを使ってウェブサイトのアカウントとパスワードを連携し、ユーザーが素早く選択できるようにしていました。
Shared Web Credentials は、アカウントとパスワードの読み取りだけでなく、新規追加、既存のアカウント情報の変更、削除も可能です。
設定
⚠️ 設定部分は、前述のPassword AutoFill設定と同様にAssociated Domainsを必ず設定してください。
つまり、Password AutoFill機能の強化版と言えます!!
同様に、まずPassword AutoFillに必要な環境を整えてから、この「高度な」機能を使用する必要があります。
読み取り
SecRequestSharedWebCredential メソッドを使って操作を行う:
SecRequestSharedWebCredential(nil, nil) { (credentials, error) in
guard error == nil else {
DispatchQueue.main.async {
//エラーを通知
}
return
}
guard CFArrayGetCount(credentials) > 0,
let dict = unsafeBitCast(CFArrayGetValueAtIndex(credentials, 0), to: CFDictionary.self) as? Dictionary<String, String>,
let account = dict[kSecAttrAccount as String],
let password = dict[kSecSharedPassword as String] else {
DispatchQueue.main.async {
//エラーを通知
}
return
}
DispatchQueue.main.async {
//アカウントとパスワードをテキストフィールドに入力
}
}
SecRequestSharedWebCredential(fqdn, account, completionHandler)
(指定したドメインとアカウントの共有ウェブ資格情報を要求します)
-
fqdn 複数の
webcredentialsドメインがある場合、特定のものを指定するか、null を使って指定しないことができます。 -
account 指定するアカウントを検索する際に使用し、null の場合は指定しないことを意味します。

効果図。(開始時の効果図と異なることに気づいたかもしれません)
⚠️ この読み取り方法はiOS 14で非推奨となりました!
⚠️ この読み取り方法はiOS 14で非推奨となりました!
⚠️ この読み取り方法はiOS 14で非推奨となりました!
"ASAuthorizationController を使って ASAuthorizationPasswordRequest を作成する(AuthenticationServices フレームワーク)"
この方法はiOS 8〜iOS 14にのみ適用され、iOS 13以降はSign in with Appleと同じAPIである「AuthenticationServices」を使用できます。
AuthenticationServices の読み取り方法
対応環境 iOS ≥ 13
import AuthenticationServices
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//...
let request: ASAuthorizationPasswordRequest = ASAuthorizationPasswordProvider().createRequest()
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.performRequests()
//...
}
}
extension ViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let credential = authorization.credential as? ASPasswordCredential {
// credential.user、credential.passwordをテキストフィールドに入力する
}
// else if as? ASAuthorizationAppleIDCredential... Sign in with Apple
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// エラーをアラート表示
}
}

効果図では、新しい方法がフローや表示面でSign in with Appleとより良く統合できることがわかります。
⚠️ このログインは Sign in with Apple(これは別のものです) の代わりにはなりません。
アカウント情報を「パスワード」に書き込む
Deprecated されているのは読み取り部分のみで、新規追加、削除、編集の部分は従来通り使用可能です。
追加、削除、編集の部分は SecAddSharedWebCredential を使って操作します。
SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { (error) in
DispatchQueue.main.async {
guard error == nil else {
// エラーを表示
return
}
// 成功を表示
}
}
SecAddSharedWebCredential(fqdn, account, password, completionHandler)
// 共有ウェブ資格情報を追加する関数
-
fqdn は、
webcredentialsに含まれていなくても、保存したいドメインを自由に指定できます。 -
account は、新しく追加、変更、削除するアカウントを指定します。
-
削除する場合は password に
nilを渡します。 -
処理のロジック:
-
アカウントが存在し、パスワードが入力されている場合 = パスワードを変更する
-
account が存在し、password が nil の場合 = domain から account と password を削除する
-
account が存在せず、password が入力されている場合 = domain に account と password を新規追加する

⚠️ また、バックグラウンドでこっそり変更することはできず、変更するたびにユーザーに確認ダイアログが表示され、「パスワードを更新」を押したときにのみデータが実際に変更されます。
パスワードジェネレーター
最後の小さな機能、パスワードジェネレーター。
SecCreateSharedWebCredentialPassword() を使った操作。
let password = SecCreateSharedWebCredentialPassword() as String? ?? ""
// 共有ウェブ資格情報のパスワードを作成する

生成されるパスワードは英大文字・小文字と数字で構成され、「-」で区切られています(例:Jpn-4t2-gaF-dYk)。
完全なテストプロジェクトのダウンロード

不満点
もしサードパーティのパスワード管理ツール(例:1Password、LastPass)を使用している方は、キーボードのPassword AutoFillでは表示および入力が可能ですが、AuthenticationServicesやSecRequestSharedWebCredentialでは表示されないことに気づくかもしれません。この要件を満たす方法があるかは不明です。

終わり
ご覧いただきありがとうございます。また、saiday さんと街声に、この機能を教えていただき感謝します XD。
さらに、Xcode 12.5以降のシミュレーターでは録画機能が追加され、GIF形式で保存できるのがとても便利です!

シミュレーターで「Command」+「R」を押して録画を開始し、赤い点をクリックして録画を停止します。右下に表示されるプレビュー画像を「右クリック」->「Save as Animated GIF」でGIFとして保存し、そのまま記事に貼り付けられます!
Post MediumからZMediumToMarkdownを使って変換しました。



コメント