【Swift/Storyboard】iOSアプリにFirebaseを用いたGoogle認証を導入する。

## 注
この記事に記載されている情報は、2023年1月8日時点のものです。

## 概要

iOS用のネイティブアプリに、Firebaseを用いたGoogleログインを導入します。

## 環境

Version 14.2 (14C18)

```terminal
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -v
Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: arm64-apple-macosx12.0
```

以下二つのpodは、バージョンを指定していません。

```Podfile
pod 'Firebase/Auth'
pod 'GoogleSignIn'
```

## 参考Document

- AppleプラットフォームでのGoogleサインインを使用した認証

https://firebase.google.com/docs/auth/ios/google-signin

- Firebaseでユーザーを管理する

https://firebase.google.com/docs/auth/ios/manage-users

- firebase/FirebaseUI-iOS

https://github.com/firebase/FirebaseUI-iOS

## 手順

0からアプリを作成するとして、主に以下の手順で作業を行います。

----

1. Xcode上で新しくプロジェクトを作成する（割愛）
1. Podfileをinitializeし、必要なライブラリをインストールする（割愛）
1. Firebase側のセットアップを行う(参考記事を添付)
1. AppDelegate.swift / ViewController.swiftで必要なコーディングを行う
1. Main.storyboardで必要なオブジェクトを配置し、作成した認証メソッドとの紐付けを行う。

----

基本的に、参考Documentに記載したコードをコピペすれば問題なく動きます。
また、手順の1,2に関しては、この記事の本題からは少し逸れるため割愛します。

cocoapods周りでエラー等が発生して解決できない場合は、ご相談ください。

Firebase側のセットアップに関しては、以下の記事の
<b>「AuthenticationでGoogleアカウントでログインする方法」</b>
という項目に、画像付きでわかりやすくまとめられています。

https://tech.amefure.com/swift-firebase-authentication-google

それでは、4,5のコードを掲載します。

## 実際のコード

```swift
// AppDelegate.swift

import UIKit
// for Auth
import Firebase
import GoogleSignIn

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FirebaseApp.configure() // for Auth

        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

    func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
        return GIDSignIn.sharedInstance.handle(url)
    }
}
```

```swift
# ViewController.swift

import UIKit
// https://github.com/firebase/FirebaseUI-iOS/tree/master/FirebaseAuthUI
import Firebase
import GoogleSignIn

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func didTapSoginButton(_ sender: Any) {
        auth()
    }

    private func auth() {
        guard let clientID = FirebaseApp.app()?.options.clientID else { return }
        let config = GIDConfiguration(clientID: clientID)

        GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in
            if let error = error {
                print("GIDSignInError: \(error.localizedDescription)")
                return
            }

            guard let authentication = user?.authentication,
                  let idToken = authentication.idToken else { return }

            let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: authentication.accessToken)
            Auth.auth().signIn(with: credential) { (authResult, error) in
              if let error = error {
                // サインインに失敗した場合
                // エラーハンドリング
                print(error.localizedDescription)
              } else {
                // サインインに成功した場合
                  self.performSegue(withIdentifier: "loginSucceed", sender: nil)
              }
            }

        }
    }

}
```

ご覧いただくとわかりますが、公式のほぼコピペです。

Main.storyboardの方の設定ですが、
デフォルトで設定されているViewControllerに対して一つViewオブジェクトを設置し、そのViewオブジェクトに対してカスタムクラスに「GIDSignInButton」を、
またTouch Up Insideのタイミングで、ViewController.swiftに定義されている「didTapSoginButton」という処理を紐づけるよう設定してください。

※タイポしていますね。。正しくは「didTapLoginButton」

|GIDSignInButton |didTapSoginButton
|- |-
|![スクリーンショット 2023-01-08 23.13.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684929/b5b6704b-871f-a176-0687-ed80d1d35c8c.png) |![スクリーンショット 2023-01-08 23.14.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684929/b1cbffee-0e5e-e0fe-77fa-730adb4ed233.png)

ここまで設定が終われば、実際にビルドしてみた際、サインイン処理が実行されていることがわかります。

```console
// デバッグモード
(lldb) po Auth.auth().currentUser
▿ Optional<FIRUser>
  - some : <FIRUser: 0x600002d87e80>
```

Firebaseを見ても、認証の際に選択したグーグルアカウントのユーザーが追加されている事がわかりますね。

![スクリーンショット 2023-01-08 23.23.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684929/a09afcad-8437-5f4d-005a-d45292ae66d9.png)

メソッド名がややこしいのですが、
Signinメソッドは選択されたアカウントのユーザーが存在しない場合は自動でFirebase側にレコードを作成してくれるため、`signup_or_signin`みたいな役割を持つメソッドだと解釈すれば良さそうです。