【Swift】FirebaseUIを使ってログイン機能を実装しよう

今回は[FirebaseUI](https://firebase.google.com/docs/auth/ios/firebaseui?hl=ja)を使ってログイン機能を実装しました！FirebaseUIを用いると簡単にGoogle, Facebook, Twitter, メール, 電話番号認証を実装することができます。(Twitterと電話番号認証は今回は割愛します。)今回はインストールの部分から紹介したいと思います。

##完成形
画面上の"Confirm"ボタンをタップすると認証画面に遷移し、認証が成功すると"Success"というラベルが表示されます。
![ダウンロード (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/9d7ad0fd-88cb-db10-aae6-74f9c277528d.gif)

##準備
はじめにXcodeプロジェクトの作成を行います。今回はFirebaseUISampleという名前にしておきます。

<img width="725" alt="スクリーンショット 2019-08-02 15.32.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/6a5a3827-2d2b-3924-352f-ce5754825af7.png">

次にストーリーボードを編集します。以下の画像のようにパーツを配置し、ViewController同士をSegueで繋いでください。**この際Segueのidentifierに"toNextView"とつけるのを忘れないでください。**

<img width="468" alt="スクリーンショット 2019-08-02 15.37.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/dcb01ce9-9d19-59f4-43f5-b2d7e744cde4.png">

##Firebaseプロジェクトの作成とXcodeプロジェクトとの関連付け
FirebaseConsoleにアクセスし、【プロジェクトを追加】ボタンから新たなプロジェクトを作成します。プロジェクト作成の手順は以下の通りです。

1. プロジェクト名の設定(今回はFirebaseUISample)
2. Googleアナリティクスを設定するかどうかの選択
3. (Googleアナリティクスを設定した場合のみ)Googleアナリティクスアカウントの選択または作成

プロジェクトを作成したら次にXcodeプロジェクトとの関連付けを行います。この手順は以下の通りです。

1. アプリの登録
2. 設定ファイルのダウンロード
3. FirebaseSDKの追加
4. 初期化コードの追加
5. アプリを実行してインストールを確認

###アプリの登録
【Project OverView】からiOSを選択し、アプリを追加します。iOSバンドルIDには【プロジェクトの設定】の【General】→【Identity】の**Bundle Identifier**を設定してください。

<img width="633" alt="スクリーンショット 2019-08-02 15.57.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/321c94e0-ca6c-d779-8726-106cdc4c4719.png">

<img width="1135" alt="スクリーンショット 2019-08-02 16.01.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/4ee07261-7188-33e2-becb-d1a4d5c5538c.png">

###設定ファイルのダウンロード
GoogleService-Info.plistをダウンロードし、Xcodeプロジェクトに追加します。この設定ファイルを何度かダウンロードした場合、GoogleService-Info(1).plistのような形式になる場合がありますが、この形式だとうまくいかないことがあるので注意してください。また、プロジェクトごとにその都度設定ファイルをダウンロードする場合、Xcodeプロジェクトに追加する際に`copy items if needed`のチェックは外しておくといいと思います。
<img width="270" alt="スクリーンショット 2019-08-02 23.25.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/a8c373a9-c37a-488d-0e17-5d6d6b942166.png">

###FirebaseSDKの追加
CocoaPodsを使用してFirebaseSDKを追加していきます。ターミナルで以下のコマンドを実行しPodFileを作成します。

```terminal
$pod init
```
次にPodFileに`pod 'FirebaseUI'`を書き込みます。その後ターミナルで以下のコマンドを実行してください。

```terminal
$pod install
```

###初期化コードの追加

```swift
import UIKit
import Firebase //Firebaseモジュールのインポート

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?

  func application(_ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions:
      [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure() //Firebaseの機能を使う際は必須
    return true
  }
}
```

###アプリを実行してインストールを確認
Xcodeプロジェクトを実行し、インストールを確認することができたらコンソールへ移動します。(少し時間がかかることがあります。)

##メール認証
メール認証を有効にするには、Firebaseコンソールから【Authentication】→【ログイン方法】→【メール/パスワード】を選択し、有効にするだけです。**この際、保存するボタンを押し忘れないように注意してください。**
<img width="1335" alt="スクリーンショット 2019-08-03 0.00.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/f1705a3b-3524-5e6b-57e0-d80ef2763dca.png">

##Google認証
Google認証を有効にするには、まずメール認証と同様にFirebaseコンソールから【Authentication】→【ログイン方法】→【Google】を選択し、有効にします。その後Xcodeプロジェクトで追加した`GoogleService-Info.plist`を選択し、その中の`REVERSED_CLIENT_ID`の値をコピーします。その後コピーした値をURLスキームに追加します。Xcodeプロジェクトの【Info】→【URLTypes】の順に選択し、"+"ボタンから追加してください。

<img width="1138" alt="スクリーンショット 2019-08-03 0.16.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/e3ad7506-4612-5fd6-3914-003fc647e101.png">

##Facebook認証
Facebook認証を行うにはFacebook for Developersへの登録が必要です。まずは他の認証方法と同様にFirebaseコンソールから【Authentication】→【ログイン方法】→【Facebook】を選択し、有効にします。アプリケーションIDとアプリシークレットを入力する必要があるので、Facebook for Developersコンソールの【設定】→【ベーシック】からこれらの値を入手し、入力します。**この際コンソール上でapp secretの値をコピーする際は必ず右にある表示ボタンを押した後にコピーしてください。**

<img width="1398" alt="スクリーンショット 2019-08-03 0.29.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/c52024ce-f6de-797a-ea5d-d75cc10d0f5c.png">


次にFirebaseコンソールのOAuthリダイレクトURIをFacebookアプリの設定に追加します。Facebookコンソールの【プロダクト+】から【Facebookログイン】を、アプリのプラットフォームにはiOSを選択し、クイックスタートの2~4までを実行してください。バンドルIDにはXcodeプロジェクトのBundleIdentifierを設定し、シングルサインオンを有効にします。その後Info.plistを右クリックして開き、【Open As Source Code】を選択してXMLスニペットをファイルの本文にコピー&ペーストしてください。(`<dict>...</dict>`で囲まれている部分にペーストしてください。)

<img width="607" alt="スクリーンショット 2019-08-03 1.04.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/c9276304-ba80-eee8-0d4c-de520624ede9.png">

2~4までの設定が終わったらFacebookコンソールの【Facebookログイン】→【設定】→【有効なOAuthリダイレクトURI】にFirebaseコンソールのOAuthリダイレクトURIをコピー&ペーストしてください。**繰り返しにはなりますが、変更を保存するのを忘れないようにしてください。**

<img width="1393" alt="スクリーンショット 2019-08-03 1.08.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/74f5d561-72b6-de48-351c-597a4a306159.png">


その次に【プロジェクトの設定】→【Capabilities】画面で、Xcode プロジェクトのキーチェーン共有を有効にします。

最後にXcodeで`[fb]+アプリケーションID`をURLスキームに追加すれば完了です。(なぜかFacebookの設定を行なった後にGoogleのURLスキームが消えていることがあるので注意してください。)

<img width="1138" alt="スクリーンショット 2019-08-03 0.16.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/c0356543-9f36-3857-3d21-624ac462962b.png">

##コーディング
「AppDelegate.swift」と「ViewController.swift」を以下のように編集してください。
`FirebaseApp.configure()`はドキュメントでは`didFinishLaunchingWithOptions`メソッドの中に記述してありましたが、うまくいかなかったので下記のようにコードを書き換えました。

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

@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 {      
        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
        }
        return false
    }
    
 }
```

```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(),
        FUIEmailAuth()
    ]

    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: "toNextView", sender: nil)
        } else {
        //失敗した場合
            print("error")
        }
    }
}

```

##よくありがちなエラー
###1. Google認証で403エラーが出る
解決策: **プロジェクトのサポートメール、アプリケーション名を設定する**
Error画面のLearn moreのリンクからOAuth設定画面行くことができるので、そちらから設定を行い、保存ボタンを押して再度認証するとうまく機能します。

###2. Facebook認証が機能しない
解決策: **app secretの値をコピーする際に表示ボタンを押した後にコピーしたかを確認する**
Xcodeプロジェクト、Firebaseコンソール、Facebookコンソールと確認するところは多いですが、それぞれ確認してみてください。また、一度GoogleアカウントとFacebookアカウントのメールアドレスが同じである場合は、一度Google認証に成功するとFacebook認証を行うことができません。どちらでも認証することができるかどうか試す場合は一度FirebaseコンソールでGoogle認証を行なった際に作成したユーザーを削除してから試してみてください。