[Xcode15.1]SwiftUIでFirebaseAuthentcationを使ってログイン機能を実装してみた

# はじめに
Firebaseを使ってログイン機能を実装している実装はUIkitを使った記事ならたくさん出てきましたがSwiftUIはあまり多くはなかったので書いてみようと思いました。
また、公式のドキュメント
(https://firebase.google.com/docs/auth/ios/start?hl=ja)
に慣れるために公式ドキュメントを用いて書いていきます。

## 前提条件
Firebaseのアカウント(Googleアカウントがある)
メール、パスワードでのログイン

## 流れ
(Firebase側の操作です)
1. アプリをFirebaseに接続する(下準備)
2. FirebaseSDKを初期化する
3. 認証状態を判定する
4. ログイン機能を実装する
5. おまけ

### アプリをFirebaseに接続する(下準備)
1. Firebaseのトップページ (https://console.firebase.google.com/u/0/?hl=ja) に遷移
2. プロジェクトを作成から順番に名前、Googleアナリティクスの順に設定
3. Authentication→始めるをクリック
4. メールを有効にする
5. Authenticationをクリックしたところまで戻ってIOS+って書いてあるところをクリック
6. AppleバンドルIDをXcodeで調べ、入力し、設定ファイルをダウンロード

(ここからXcode側の操作です)
7. ダウンロードした`GoogleService-Info.plist`ファイルを`<アプリ名>App`と同じ階層に保存する(ドラッグアンドドロップでいいです)
8. macの画面上部のFileというところから`Add Pacage ...`をクリックする
9. 右上の検索欄に(https://github.com/firebase/firebase-ios-sdk)をいれ、Add Pacageを押す

 ### 補足
 **そもそもFirebase SDKとは？**
ざっくりいうと「FirebaseをSwiftで使う上でこれだけあればなんとかなるやろ！」みたいなもの
ゲームで言うと
- スマブラのDLCがまとめて購入できるファイターパス
- ポケモンのディンカイサーフパオカミウーラのレンタルパーティ
みたいな感じです(例え下手でごめんなさい)

みたいなイメージです
 
 **AppleバンドルIDの調べ方**
 ![スクリーンショット 2024-08-31 20.13.39.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763749/3d91496f-25d2-31e6-85d6-42523f413931.jpeg)
Xcodeの左のナビゲーション版の一番上にあるアプリ名をクリックしたら下記画像にあるIdentityを探します(Generateのところにあります)
![スクリーンショット 2024-08-31 20.18.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763749/b6e58c88-ba08-88e8-ae9b-846b73f88520.png)
少し消えてますが、Build Identifierのところに書かれているものがAppleバンドルIDです！

### FirebaseSDKを初期化する
![2024-08-31 20.28の画像.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763749/b16b2022-c138-b6d6-727b-0ae0e66b97f1.jpeg)
公式ドキュメントにはこう書かれてます
SwiftUIには`AppDeligate`がないので`<アプリ名>App`のファイルに記述します。

```swift
import SwiftUI
// importを忘れずに
import FirebaseCore

@main
struct <アプリ名>App: App {
  // ここから記述
  init() {
    FirebaseApp.configure()
  }
  //　ここまで記述
    var body: some Scene {
        WindowGroup {
            Login()
        }
    }
}

```

### 認証状態を判定する
![2024-08-31 20.34の画像.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763749/f19497d1-8551-07ab-8b33-d06122ee1aad.jpeg)
公式ドキュメントにはこう書かれてます。
「認証状態をリッスンする」とは「ログインしてるかしてないかの判定」のことです。(小泉進次郎並感)
記述の場所は各種ViewModelですのでこの後記述していきます。
`Auth.auth().addStateDidChangeListener`はFirebaseの関数で「ユーザーがログインしたりログアウトしたりしたときに、その状態の変化を通知」する役割があります

### 各種機能を実装する(ログイン、新規登録、ログアウト、パスワードのリセット)
ここまできたらあとはドキュメントに書かれているコードをひとつひとついい感じに書いていくだけです！
 私は下記サイトを参考にしました
(https://zenn.dev/joo_hashi/articles/3e28d2f6e46674)

**ログイン処理**
ViewModel
```swift
import Foundation
import FirebaseAuth

class LoginViewModel: ObservableObject {
  @Published var email: String = ""
  @Published var password: String = ""
  @Published var isAuth:Bool = false
  
  init() {
    //    インスタンスが生成されたら認証情報を確認
    observeAuthChanges()
  }
  //  認証状態を確認する
  private func observeAuthChanges() {
    Auth.auth().addStateDidChangeListener { [weak self] _, user in
      DispatchQueue.main.async {
        self?.isAuth = user != nil
      }
    }
  }
  //  ログインする関数
  func signIn(email: String, password: String) {
    Auth.auth().signIn(withEmail: email, password: password) { [weak self] result, error in
      DispatchQueue.main.async {
        if result != nil, error == nil {
          self?.isAuth = true
        }
      }
    }
  }
}

```
View
```swift
import SwiftUI

struct Login: View {
  @State var email: String = ""
  @State var password: String = ""
  @StateObject var loginViewModel = LoginViewModel()
  var body: some View {
    NavigationStack {
        TextField("email", text: $email)
        
        SecureField("password", text: $password)
        
        NavigationLink {
          SignUp().toolbar(.hidden)
        } label: {
          Text("会員登録へ")
        }
        
        NavigationLink {
          RessetPassword()
        } label: {
          Text("パスワードを忘れた方")
        }
        
        Button(action: {
          loginViewModel.signIn(email: email, password: password)
        }) {
          Text(AppConstant.login)
        }
        NavigationLink(
          destination: Home().toolbar(.hidden),
          isActive: Binding(
            get: { loginViewModel.isAuth },
            set: { loginViewModel.isAuth = $0 }
          ),
          label: {
            EmptyView()
          })
      }
    }
  }
}
```
みたいな感じで書いていきます。
新規会員登録、パスワードのリセットなどの機能も同様に公式ドキュメントに書いてある関数を使えばできます。

### おまけ
**Firebaseのメール、パスワードでログインする機能の主要な関数まとめ**
`Auth.auth().addStateDidChangeListener` 
ユーザーがログインしたりログアウトしたりしたときに、その状態の変化を通知

`Auth.auth().signIn(withEmail: email, password: password)`
サインインする関数

`Auth.auth().createUser(withEmail: email, password: password)`
新規会員登録

`Auth.auth().sendPasswordReset(withEmail: email) `
指定のメールアドレスにリセットのメールを飛ばす