SDK
- iOS 13.0+
 - Xcode 11.3+
 
フレームワーク
- Authentication
Services  
概要
このサンプルアプリ「Juice」は、AuthenticationServicesフレームワークを使用し、ユーザーが自分のApple IDを使ってアカウントを設定しサインインするためのインターフェイスを提供します。ユーザーがアプリ用のアカウントを作成・設定するためのフォームを表示し、「Appleでサインイン」によりユーザーのApple IDの認証を行って、ユーザーのアカウントデータを表示します。
iOS 12以前での「Appleでサインイン」の実装については、他のプラットフォームでの「Appleでサインイン」の利用(英語)を参照してください。
サンプルコードプロジェクトの設定
サンプルコードプロジェクトを設定するには、Xcodeで以下の手順を実行してください。
- 
						
「Signing & Capabilities」パネルで、ユニークなバンドルIDの設定(英語)を行います(バンドルIDを変更しないと次に進めません)。
 - 
						
Apple IDアカウントの追加(英語)と、チームへのターゲットの割り当て(英語)を行い、Xcodeがプロビジョニングプロファイルを使用して「Appleでサインイン」を有効化できるようにします。
 - 
						
2ファクタ認証が設定されたApple IDでサインインを行う実行先を、「Scheme」ポップアップメニューから選択します。
 - 
						
必要に応じて、「Signing & Capabilities」パネルで「Register Device」をクリックし、プロビジョニングプロファイルを作成します。
 - 
						
ツールバーで「Run」をクリックするか、または「Product」>「Run(⌘R)」を選択します。
 
「Appleでサインイン」ボタンの追加
						サンプルアプリでは、Loginによって、ログインフォームと「Appleでサインイン」ボタン(ASAuthorization(英語))がビュー階層の中に表示されます。ビューコントローラ自身もボタンのターゲットとして追加され、ボタンがタッチアップのイベントを受け取ったときに起動するアクションを指定します。
					
func setupProviderLoginView() {
    let authorizationButton = ASAuthorizationAppleIDButton()
    authorizationButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside)
    self.loginProviderStackView.addArrangedSubview(authorizationButton)
}
							
重要
							Storyboardに「Appleでサインイン」ボタンを追加する場合、Xcodeの「Identity」インスペクタで、このボタンのクラスの値をASAuthorizationに設定する必要があります。
						
Apple IDによる認可のリクエスト
						ユーザーが「Appleでサインイン」ボタンをタップしたら、ビューコントローラはhandle関数を呼び出します。この関数は、ユーザーの氏名とEメールアドレスに対する認可リクエストを実行して認証フローを開始します。するとシステムは、ユーザーが自分のApple IDでデバイスにサインインしているかどうかをチェックします。ユーザーがシステムレベルでサインインしていない場合、アプリはユーザーが「設定」でApple IDによるサインインを行うよう、メッセージを表示します。
					
func handleAuthorizationAppleIDButtonPress() {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest()
    request.requestedScopes = [.fullName, .email]
    
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self
    authorizationController.performRequests()
}
							
重要
「Appleでサインイン」を利用するには、ユーザーは2ファクタ認証を有効にし、アカウントへのアクセスの安全を確保する必要があります。
						認可コントローラはpresentation(英語)関数を呼び出し、「Appleでサインイン」に関する情報をモーダルシートでユーザーに提示するためのウインドウをアプリから取得します。
					
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    return self.view.window!
}
							
ユーザーが自分のApple IDを使用してシステムレベルでサインインしている場合、「Appleでサインイン」の機能を説明するシートが表示され、続いて、ユーザーが自分のアカウントの情報を編集できるシートが表示されます。ユーザーは、自分の姓名や、連絡先情報のEメールアドレスを変更したり、Eメールアドレスをアプリに対して非公開にすることができます。ユーザーが、Eメールアドレスをアプリに対して非公開にすることを選択した場合、Appleは代理のEメールアドレスを生成し、ユーザーの私的なEメールアドレスにEメールを転送します。最後に、ユーザーはApple IDのパスワードを入力し、「Continue」をクリックしてアカウントを作成します。
ユーザー認証情報の処理
						認証に成功したら、認可コントローラはデリゲート関数authorization(英語)を呼び出します。アプリはこの関数を使ってユーザーのデータをキーチェーンに保存します。
					
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    switch authorization.credential {
    case let appleIDCredential as ASAuthorizationAppleIDCredential:
        
        // Create an account in your system.
        let userIdentifier = appleIDCredential.user
        let fullName = appleIDCredential.fullName
        let email = appleIDCredential.email
        
        // For the purpose of this demo app, store the `userIdentifier` in the keychain.
        self.saveUserInKeychain(userIdentifier)
        
        // For the purpose of this demo app, show the Apple ID credential information in the `ResultViewController`.
        self.showResultViewController(userIdentifier: userIdentifier, fullName: fullName, email: email)
    
    case let passwordCredential as ASPasswordCredential:
    
        // Sign in using an existing iCloud Keychain credential.
        let username = passwordCredential.user
        let password = passwordCredential.password
        
        // For the purpose of this demo app, show the password credential as an alert.
        DispatchQueue.main.async {
            self.showPasswordCredentialAlert(username: username, password: password)
        }
        
    default:
        break
    }
}
							
メモ
							実装の際には、デリゲート関数ASAuthorizationが、ユーザーIDに含まれるデータを使用してシステム内にアカウントを作成する必要があります。
						
						認証に失敗した場合、認可コントローラはデリゲート関数authorization(英語)を呼び出し、エラー処理を行います。
					
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    // Handle error.
}
							
						システムがユーザーを認証したら、アプリはResultを表示します。これはユーザーが入力した氏名やEメールアドレスなど、フレームワークが要求するユーザー情報を示すものです。また、このビューコントローラは「サインアウト」ボタンを表示し、ユーザーデータをキーチェーンに保存します。ユーザーが「サインアウト」ボタンをタップしたら、アプリはビューコントローラとキーチェーンからユーザー情報を削除し、Loginをユーザーに提示します。
					
既存の認証情報のリクエスト
						Login関数は、Apple IDとiCloudキーチェーンパスワードの両方を要求することによって、ユーザーが既存のアカウントを持っているかどうかを確認します。handleと同様、認可コントローラはプレゼンテーションコンテンツプロバイダを設定し、Loginオブジェクトにデリゲートします。
					
func performExistingAccountSetupFlows() {
    // Prepare requests for both Apple ID and password providers.
    let requests = [ASAuthorizationAppleIDProvider().createRequest(),
                    ASAuthorizationPasswordProvider().createRequest()]
    
    // Create an authorization controller with the given requests.
    let authorizationController = ASAuthorizationController(authorizationRequests: requests)
    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self
    authorizationController.performRequests()
}
							
						デリゲート関数authorizationは、認証情報がApple ID(ASAuthorization(英語))とパスワード認証情報(ASPassword(英語))のどちらであるかを確認します。認証情報がパスワードだった場合、システムはユーザーが既存のアカウントで認証できるよう、メッセージを表示します。
					
起動時のユーザー認証情報のチェック
						サンプルアプリは、「Appleでサインイン」のユーザーインターフェイスを必要に応じて表示しているだけです。起動直後、アプリのデリゲートは、App関数で、保存されたユーザー認証情報のステータスをチェックします。
					
						get(英語)関数は、キーチェーンに保存されたユーザーIDのステータスを取得します。ユーザーがそのアプリに対する認可を与えていれば(ユーザーがデバイスのApple IDでサインインしている場合など)、アプリは実行を継続します。ユーザーがそのアプリに対する認可を与えていない場合や、認証情報のステータスが見つからない場合は、アプリはshow関数を呼び出してログインフォームを表示します。
					
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    appleIDProvider.getCredentialState(forUserID: KeychainItem.currentUserIdentifier) { (credentialState, error) in
        switch credentialState {
        case .authorized:
            break // The Apple ID credential is valid.
        case .revoked, .notFound:
            // The Apple ID credential is either revoked or was not found, so show the sign-in UI.
            DispatchQueue.main.async {
                self.window?.rootViewController?.showLoginViewController()
            }
        default:
            break
        }
    }
    return true
}