【Swift】FirebaseUIでユーザー認証画面を実装する

アプリ開発者にとって心強い味方になりつつあるのがFirebaseです。
ここで説明するまでもないですが、色々な機能が追加されています。
その中で私のアプリ開発の中で使用したFirebaseUIについて紹介します。

#FirebaseUIとは？
FirebaseUIはGoogleが用意してくれているライブラリで、「メール」「電話番号」「Google」「Facebook」「Twitter」のユーザー情報による認証、そして認証画面を容易に実装できます。
下の画像のような認証ボタンが並んでいるViewの表示やFirebase上でのアカウント作成を行ってくれます。
<img width="375" alt="firebaseui-ios.png" src="https://qiita-image-store.s3.amazonaws.com/0/295440/fcbb8c02-4c45-c2aa-9de3-9dad02505047.png">


#インストール
前提として、Firebaseプロジェクトが作成済みで、Xcodeプロジェクトとの関連付けも完了しているとします。
（もしまだの方は公式ドキュメントを参考に準備して下さい）
[Googleの公式ドキュメント](https://firebase.google.com/docs/ios/setup?hl=ja)

インストールにはCocoaPodsを利用することが推奨されています。
Podfileに「pod 'FirebaseUI'」を追加して、pod installすればOKです。
もし特定のプロバイダのみ使用したい場合は、「pod 'FirebaseUI/Auth'」と必要なプロバイダのみ追加することも出来ます。
使用可能なプロバイダは以下の4つです。（メールはデフォルトで入っています）
pod 'FirebaseUI/Google'
pod 'FirebaseUI/Facebook'
pod 'FirebaseUI/Twitter'
pod 'FirebaseUI/Phone'

#準備
インストールが完了したら、各プロバイダの認証を行うための準備が必要になります。
各プロバイダの準備手順を記載するので、必要なプロバイダのみ参照して下さい。

###メール
メール認証の準備は非常に簡単です。
Firebaseコンソールから「Authentication」→「ログイン方法」→「メール/パスワード」を選択し、有効にした後保存すればOKです。
![スクリーンショット 2018-10-18 15.47.29.png](https://qiita-image-store.s3.amazonaws.com/0/295440/ee4d67b6-0b54-fd0b-9b16-cf535049664f.png)

###Google
FirebaseをGoogleが提供しているだけあって、この準備も簡単です。
まずメール認証と同様に、FirebaseコンソールでGoogle認証を有効にします。
次にXcodeでの作業を行います。
Firebaseのプロジェクト作成時に追加したGoogleService-Info.plistの中から「REVERSED_CLIENT_ID」の値をコピーします。
![スクリーンショット 2018-10-18 15.56.19.png](https://qiita-image-store.s3.amazonaws.com/0/295440/c5f6b4e5-4559-03e5-76d1-a1303fd623a8.png)
コピーした値をURLスキームに追加します。
URLスキームを追加する場所は以下の画像を参考にしてみて下さい。
![スクリーンショット 2018-10-18 16.03.58.png](https://qiita-image-store.s3.amazonaws.com/0/295440/23b8657a-0528-a283-0b22-4b3e2c778043.png)

###Facebook
**前提：Facebook認証を行うにはFacebook for Developersへの登録が必要です。**
Facebookによる認証でも、FirebaseコンソールでFacebook認証を有効にするところから始めましょう。
有効にすると、アプリケーションIDとアプリシークレットを入力出来るようになります。
ここには、Facebook for Developersのコンソールから取得出来る値を入力します。
アプリシークレットIDとアプリシークレットの取得場所は以下の画像を参考にして下さい。
![スクリーンショット 2018-10-18 16.13.11.png](https://qiita-image-store.s3.amazonaws.com/0/295440/0f062940-6ead-5613-3e04-15f438b0efdb.png)

次にFirebaseコンソールに表示されているOAuthリダイレクトURLをFacebookアプリの設定に追加します。
ただ、Facebook for Developersのコンソールをいくら探してもOAuthリダイレクトURLを入力する箇所が見つからないと思います。
（私もここでハマりました）
FacebookアプリにOAuthリダイレクトURLを設定するには、「プロダクト+」から「Facebookログイン」を追加する必要があります。
![スクリーンショット 2018-10-18 16.35.19.png](https://qiita-image-store.s3.amazonaws.com/0/295440/4c2064da-83e1-afeb-8cfd-5384326bec10.png)

「Facebookログイン」の「設定」をクリックすると以下の様な画面が表示されるかと思います。
![スクリーンショット 2018-10-18 16.46.06.png](https://qiita-image-store.s3.amazonaws.com/0/295440/a780db15-e4f8-f8ec-67ac-0c49df2c4199.png)
**この9つの手順の内、2・3・4のみ行って下さい。**
1に関してはFirebaseUIをインストールした時点で「FBSDKLoginKit」もインストールされているので不要です。
5以降はFirebaseUIを使用する場合、設定しなくてもOKです。

設定が完了するとFacebook for Developersのコンソールの「Facebookログイン」の設定画面でOAuthリダイレクトURLを入力する箇所が出てくるので、そこにFirebaseコンソールに表示されているURLを入力します。
![スクリーンショット 2018-10-18 16.53.48.png](https://qiita-image-store.s3.amazonaws.com/0/295440/efaaf069-056e-9581-f5a5-4044f1776f6b.png)


最後にXcodeで「(fb)+アプリケーションID」をURLスキームに追加すれば完了です。
![スクリーンショット 2018-10-18 17.12.08.png](https://qiita-image-store.s3.amazonaws.com/0/295440/7a039161-8b9b-cd27-b5c2-c2a41fe4d8c4.png)

###Twitter
Facebook同様、Twitterも開発者アカウントを取得することが必要になりますが…
審査があり、その審査のために英語を書く必要があるので、結構めんどうです。
以下の記事を参考に進めてもらえれば良いかと思います。
https://qiita.com/tdkn/items/521686c240b0c5bc6207

Twitter Developerの審査を通過したら、Firebaseでの準備を進めます。
例によって、FirebaseコンソールでTwitter認証を有効にするところから始めましょう。
Firebaseコンソールに表示されているコールバック URLをコピーし、Twitter Developerのコンソールに移動したら新しいアプリの作成（Create an app）をクリックします。
すると以下の画像のような詳細情報を入力するページが表示されます。
ここの「Callback URLs」に先程コピーしたURLを入力します。
![スクリーンショット 2018-10-18 17.31.23.png](https://qiita-image-store.s3.amazonaws.com/0/295440/10aabd6a-0c80-8f8d-5502-f01ffe3e970a.png)

また、その他の必須項目（required）を入力し、ページ下部の「Create」をクリックします。
アプリの作成が完了したら、「Keys and tokens」からAPIキーとAPIシークレットキーをコピーし、Firebaseコンソールで入力します。
![スクリーンショット 2018-10-18 17.36.23.png](https://qiita-image-store.s3.amazonaws.com/0/295440/e9acbbdc-35dc-8d09-d8ba-4b7885288672.png)
ここでもう一度Twitter Developerのコンソールに戻って、先程作成したアプリの設定を変更します。
以下の記事にもある通り、「Callback URLs」に自身のAPIキーを入力する必要があります。
https://qiita.com/Takumi_Mori/items/4a59ab1751a3a1e6b501
入力すると「Callback URLs」が2つになります。
![スクリーンショット 2018-10-18 17.50.14.png](https://qiita-image-store.s3.amazonaws.com/0/295440/37ad0045-a5f2-fc4a-7c5b-877b3cac1da9.png)
最後にXcodeで「twitterkit-APIキー」をURLスキームに入力したら完了です。![スクリーンショット 2018-10-18 17.52.51.png](https://qiita-image-store.s3.amazonaws.com/0/295440/3192d39b-0e4f-1c44-218b-6f9e2967b062.png)

##電話番号
最後に電話番号による認証です。
今回もまずはFirebaseコンソールから電話番号認証を有効にして下さい。
Firebaseの電話番号認証には「サイレントAPNs通知」「reCAPTCHA検証」の2つがあります。
「サイレントAPNs通知」にはApple Developer Member Centerから入手できるキーファイル、
キーID、チームIDが必要になります。
詳しくは以下の公式ドキュメントを参照して下さい。
https://firebase.google.com/docs/auth/ios/firebaseui?hl=ja

「reCAPTCHA検証」は、Google認証で行ったURLスキームの設定を行ってあれば使用できます。


#実装
ここまでの準備が完了したら、実装を行います。
実装の説明には簡単なアプリの構成を使ってみます。
具体的には、起動後のトップページに認証ボタンを配置。
そのボタンをタップするとFirebaseUIのViewが表示され、認証に成功すると次のViewに遷移する構成です。
ストーリーボードでは以下の画像のようになります。
![スクリーンショット 2018-10-19 11.06.00.png](https://qiita-image-store.s3.amazonaws.com/0/295440/e0e5de80-b187-c291-07b4-81f75af497e8.png)

この構成を実現するには「AppDelegate.swift」「ViewController.swift」が最低限必要です。
まずは「AppDelegate.swift」のコードを以下に載せます。

```swift:AppDelegate.swift
import UIKit
import Firebase
import FirebaseUI
import TwitterKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    override init() {
        super.init()
        // Firebase関連の機能を使う前に必要
        FirebaseApp.configure()
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        //Twitter認証用のイニシャライズ
        TWTRTwitter.sharedInstance().start(withConsumerKey:"twitterのAPIキー",consumerSecret:"twitterのAPIシークレット")
        return true
    }
    
    // facebook&Google&電話番号認証時に呼ばれる関数
    func application(_ app: UIApplication, open url: URL,
                     options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
        let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as! String?
        // GoogleもしくはFacebook認証の場合、trueを返す
        if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication) ?? false {
            return true
        }
        // 電話番号認証の場合、trueを返す
        if Auth.auth().canHandle(url) {
            return true
        }
        return false
    }
    
    // 電話番号認証の場合に通知をHandel出来るかチェックする関数
    func application(_ application: UIApplication,
                     didReceiveRemoteNotification notification: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        if Auth.auth().canHandleNotification(notification) {
            completionHandler(.noData)
            return
        }
        // エラーの時の処理を書く
    }
}
```

重要な所をいくつか解説していきます。
`FirebaseApp.configure()`はFirebaseUIに限らずFirebase関連の機能を使う前に宣言が必要です。
今回の場合は`init()`内で宣言しています。

`TWTRTwitter.sharedInstance().start`はtwitterの初期化に必要です。
Facebook認証では同等の処理が必要ないので、今後必要なくなるかもしれません。

`func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any])`はGoogleもしくはFacebook認証を使う場合に必要と公式ドキュメントで記載されています。
それに加えて、電話番号認証のこの関数が呼ばれます。
そのため、認証がGoogleでもFacebookでも無い場合に`if Auth.auth().canHandle(url)`を呼ぶことで、電話番号認証の時も`true`を返すように設定します。

`func application(_ application: UIApplication,
                     didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)`は通知を受け取った時の処理する時に呼ばれる関数です。
電話番号認証の場合、通知を使用するので`Auth.auth().canHandleNotification(notification)` の真偽をチェックし、trueの場合電話番号認証なので、ここでは特に処理することなく`return` します。

次に「ViewController.swift」のコードを載せます。

```swift:ViewController.swift
import UIKit
import Firebase
import FirebaseUI

class ViewController: UIViewController,FUIAuthDelegate {
    
    @IBOutlet weak var AuthButton: UIButton!

    var authUI: FUIAuth { get { return FUIAuth.defaultAuthUI()!}}
    // 認証に使用するプロバイダの選択
    let providers: [FUIAuthProvider] = [
        FUIGoogleAuth(),
        FUIFacebookAuth(),
        FUITwitterAuth(),
        FUIPhoneAuth(authUI:FUIAuth.defaultAuthUI()!),
        ]

    override func viewDidLoad() {
        super.viewDidLoad()
        // authUIのデリゲート
        self.authUI.delegate = self
        self.authUI.providers = providers
        AuthButton.addTarget(self,action: #selector(self.AuthButtonTapped(sender:)),for: .touchUpInside)
    }
    
    @objc func AuthButtonTapped(sender : AnyObject) {
        // FirebaseUIのViewの取得
        let authViewController = self.authUI.authViewController()
        // FirebaseUIのViewの表示
        self.present(authViewController, animated: true, completion: nil)
    }
    
    //　認証画面から離れたときに呼ばれる（キャンセルボタン押下含む）
    public func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?){
        // 認証に成功した場合
        if error == nil {
            self.performSegue(withIdentifier: "toTopView", sender: self)
            }
        // エラー時の処理をここに書く
        }
}
```
基本的にはコードとコメントを見てもらえれば充分なほど、簡単に実装が出来ます。
1つ注意して欲しいのは`authUI()`です。
この関数はFirebaseUIのViewから離れた時に呼ばれます。
FirebaseUIのViewから離れるのは認証に成功した時やキャンセルボタンを押した時などがあり、その時の`user`と`error`の値によって処理を設定する必要があります。
今回の場合は、`error == nil`、つまり認証に成功した時に次画面に遷移するように設定しています。

#認証結果の確認
実装が完了したらアプリをビルドし、認証を行ってみましょう。
上手く行けばコンソールに認証情報が表示されます:ok_hand:
![スクリーンショット 2018-10-22 13.47.15.png](https://qiita-image-store.s3.amazonaws.com/0/295440/1acad545-30d8-4229-e883-9712a6b61387.png)


#まとめ
アプリ開発時に使用したFirebaseUIを紹介しようと記事を作成しました。
簡単に各種プロバイダの認証画面が実装できるFirebaseUIですが、公式ドキュメントで記載が無い箇所もあるのでこの記事が皆さんの助けになれば幸いです。
また、誤りやより良い実装方法があればコメント下さい。