Firebase IT アプリ開発

【Firebase】Firebase Authenticationを使いSwiftUIでログイン機能を実装する

2021年10月31日


最近では、iOSアプリの開発をするエンジニアも増えてきてますが、
それに比例してアプリの開発をサポートするソリューションも増えてきました。その中の一つとしてFirebaseというMBaaSソリューションがあります。
これはGoogleがリリースしたもので、iOSアプリ、Androidアプリに限らずWebのバックエンドの処理をサポートしてくれるツールです。

ここでは、その中の1つであるFirebase Authenticationというツールを紹介します。
このツールはユーザー、パスワードを入力するログイン実装機能をサポートしてくれます。それだけではなく、ここでは扱いませんがソーシャルログインなどGoogleアカウントでログインする実装などもできたりします。

メールアドレスの認証の場合は、以下にメールアドレスとパスワードが保存される。

そして、それ以外でgoogleやtwitterなど使いたい場合は、以下のように選択できる。
これにより認証したときに、各プロパイダの認可サーバへアクセスしてあったら、リダイレクトして認証する仕組み

まずは実装イメージをしてみます。

  • SwiftUIでログイン画面(メールアドレスとパスワード)を実装する
  • 入力したメールアドレスとパスワードの情報をFirebaseに渡す
  • すでにアカウントがあれば認証を、なければ新規登録としてアカウント作成する
  • ログイン完了画面、失敗画面を表示する

ですね。
これに則って実装していきたいと思います。

まず簡単にViewにメールアドレスやパスワードの入力欄を作成します。
Firebase AuthenticationはFirebaseをcocoapodsに入れてライブラリをインストーすることで、
そのパッケージ内にFirebaseAuthが入っている。

import SwiftUI
import FirebaseAuth

struct NewLogin: View {
    
    @State public var mail:String = ""
    @State public var password:String = ""
    @State public var errorMessage:String = ""
    
    var body: some View {
        VStack(spacing: 30){
            // メールアドレス
            TextField("メールアドレスを入力してください",text: $mail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // パスワード
            SecureField("パスワードを入力してください",text:$password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // 認証
            Button(
                action:{
                    if(self.mail != ""){
                        self.errorMessage = "メールアドレスが入力されていません"
                    } else if(self.password != ""){
                        self.errorMessage = "パスワードが入力されていません"
                    } else {
                        // 認証する処理
                    }
                }, label:{
                    Text("ログインする")
                })
        }
    }
}

上記のように、普通にViewに入力欄を作成して、ボタンを実装し、
入力されていなかったらエラーメッセージを出して認証処理しないようになっている。

import SwiftUI
import FirebaseAuth

struct NewLogin: View {
    
    @State public var mail:String = ""
    @State public var password:String = ""
    @State public var errorMessage:String = ""
    
    var body: some View {
        VStack(spacing: 30){
            // メールアドレス
            TextField("メールアドレスを入力してください",text: $mail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // パスワード
            SecureField("パスワードを入力してください",text:$password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // 認証
            Button(
                action:{
                    if(self.mail == ""){
                        self.errorMessage = "メールアドレスが入力されていません"
                    } else if(self.password == ""){
                        self.errorMessage = "パスワードが入力されていません"
                    } else {
                        Auth.auth().createUser(withEmail: self.mail, password: self.password) { authResult, error in
                           print(authResult)
                        }
                    }
                }, label:{
                    Text("新規会員登録する")
                })
        }
    }
}

新規会員登録では、上のAuthの処理がそれにあたります。新規会員登録ボタンをクリックすると追加処理です。

Auth.auth().createUser(withEmail: self.mail, password: self.password) { authResult, error in
   print(authResult)
}                  

上記処理が走ると成功した場合、以下のようにFirebaseにメールアドレスとパスワードの会員情報が連携されます。

さて先程作成したログイン情報を用いてログインしてみます。

import SwiftUI
import FirebaseAuth

struct NewLogin: View {
    
    @State public var mail:String = ""
    @State public var password:String = ""
    @State public var errorMessage:String = ""
    
    var body: some View {
        VStack(spacing: 30){
            // メールアドレス
            TextField("メールアドレスを入力してください",text: $mail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // パスワード
            SecureField("パスワードを入力してください",text:$password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // 認証
            Button(
                action:{
                    if(self.mail == ""){
                        self.errorMessage = "メールアドレスが入力されていません"
                    } else if(self.password == ""){
                        self.errorMessage = "パスワードが入力されていません"
                    } else {
                       
                        Auth.auth().signIn(withEmail: self.mail, password: self.password) { authResult, error in
                                    if authResult?.user != nil {
                                        // ログイン成功処理
                                        print("success")
                                    } else {
                                        // ログイン失敗処理
                                        if let error = error as NSError?, let errorCode = AuthErrorCode(rawValue: error.code) {
                                            switch errorCode {
                                            case .invalidEmail:
                                                self.errorMessage = "メールアドレスの形式が正しくありません"
                                            case .userNotFound, .wrongPassword:
                                                self.errorMessage = "メールアドレス、またはパスワードが間違っています"
                                            case .userDisabled:
                                                self.errorMessage = "このユーザーアカウントは無効化されています"
                                            default:
                                                self.errorMessage = error.domain
                                            }
                                        }
                                    }
                                }
                    }
                }, label:{
                    Text("ログインする")
                })
        }
    }
}

先程アカウント登録で入力したメールアドレスとパスワードを入力して、
successとlogで表示されればログイン成功です!

次にアカウントの削除をしてみます。
→ よく皆さんのアプリなど使っていて削除するのは、自分がログイン状態でマイページから削除などがあるかと思います。
なので記載するコードとしては以下のようになります。
https://firebase.google.com/docs/auth/ios/manage-users?hl=ja

let user = Auth.auth().currentUser

user?.delete { error in
  if let error = error {
    // An error happened.
  } else {
    // Account deleted.
  }
}          

そしてログインしている状態で削除することになるので、
アカウント作成はしていると想定して、
メールアドレスとパスワード入力して「ログインする」ボタンをクリックして、
その後「アカウント削除」ボタンをクリックしてFirebaseからアカウントが削除されていることを確認します。

import SwiftUI
import FirebaseAuth

struct NewLogin: View {
    
    @State public var mail:String = ""
    @State public var password:String = ""
    @State public var errorMessage:String = ""
    
    @State public var stateLogin:Bool = false  // ログイン状態
    
    var body: some View {
        VStack(spacing: 30){
            // メールアドレス
            TextField("メールアドレスを入力してください",text: $mail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // パスワード
            SecureField("パスワードを入力してください",text:$password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // 認証
            Button(
                action:{
                    if(self.mail == ""){
                        self.errorMessage = "メールアドレスが入力されていません"
                    } else if(self.password == ""){
                        self.errorMessage = "パスワードが入力されていません"
                    } else {
                       
                        Auth.auth().signIn(withEmail: self.mail, password: self.password) { authResult, error in
                                    if authResult?.user != nil {
                                        // ログイン成功処理
                                        print("success")
                                        self.stateLogin = true
                                    } else {
                                        // ログイン失敗処理
                                        if let error = error as NSError?, let errorCode = AuthErrorCode(rawValue: error.code) {
                                            switch errorCode {
                                            case .invalidEmail:
                                                self.errorMessage = "メールアドレスの形式が正しくありません"
                                            case .userNotFound, .wrongPassword:
                                                self.errorMessage = "メールアドレス、またはパスワードが間違っています"
                                            case .userDisabled:
                                                self.errorMessage = "このユーザーアカウントは無効化されています"
                                            default:
                                                self.errorMessage = error.domain
                                            }
                                        }
                                    }
                                }
                    }
                }, label:{
                    Text("ログインする")
                })
            
            
            // アカウント削除
            Button(
                action:{
                    let user = Auth.auth().currentUser

                    user?.delete { error in
                      if let error = error {
                        // An error happened.
                      } else {
                        print("削除成功")
                      }
                    }
                }, label:{
                    Text("アカウント削除する")
                })
        }
    }
}

Firebaseからアカウントが削除されているのが確認できると思います!
成功しました!

ログイン状態は別のViewに遷移しても保持されるのかを検証。
ログインしている場合は以下でcurrentUserという値がFirebaseのUIDが入るようになっている。
なので別Viewでこの値が取れるかどうかで検証。

import SwiftUI
import FirebaseAuth

struct NewLogin: View {
    
    @State public var mail:String = ""
    @State public var password:String = ""
    @State public var errorMessage:String = ""
    
    @State public var stateLogin:Bool = false  // ログイン状態
    
    var body: some View {
        VStack(spacing: 30){
            
            if(self.stateLogin){
                LoginRetain()
            } else {
            
            // メールアドレス
            TextField("メールアドレスを入力してください",text: $mail)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // パスワード
            SecureField("パスワードを入力してください",text:$password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            // 認証
            Button(
                action:{
                    if(self.mail == ""){
                        self.errorMessage = "メールアドレスが入力されていません"
                    } else if(self.password == ""){
                        self.errorMessage = "パスワードが入力されていません"
                    } else {
                        Auth.auth().signIn(withEmail: self.mail, password: self.password) { authResult, error in
                            if authResult?.user != nil {
                                // ログイン成功処理
                                print("success")
                                self.stateLogin = true
                            } else {
                                // ログイン失敗処理
                                if let error = error as NSError?, let errorCode = AuthErrorCode(rawValue: error.code) {
                                    switch errorCode {
                                    case .invalidEmail:
                                        self.errorMessage = "メールアドレスの形式が正しくありません"
                                    case .userNotFound, .wrongPassword:
                                        self.errorMessage = "メールアドレス、またはパスワードが間違っています"
                                    case .userDisabled:
                                        self.errorMessage = "このユーザーアカウントは無効化されています"
                                    default:
                                        self.errorMessage = error.domain
                                    }
                                }
                            }
                        }
                    }
                }, label:{
                    Text("ログインする")
                })
            }
        }
    }
}



struct LoginRetain:View{
    
    var body: some View {
        Text("Login完了")
        Button(
            action:{
                let user = Auth.auth().currentUser
                var _ = print(user)
            }, label:{
                Text("状態保持テスト")
        })
   }
}

Firebaseのヘルプには以下のようにありました。
''currentUser を使用することでも、現在ログインしているユーザーを取得できます。ユーザーがログインしていない場合、currentUser は nil です。''
参照

そのためLoginRetainというViewを作成して、currentUserが取得できるかどうかをログで確認します。
ログインするボタンを押してログインできると、LoginRetainが表示され、さらに状態保持テストボタンをクリックしてcurrentUserがログで取得できているかを確認します。

実行すると、ログで取得できているのが確認できるかと思います。

別Viewに遷移してもFirebase上でログイン状態の保持をしてくれていました。

ユーザーが登録すると上のFirebase Authenticationの管理画面に情報が表示されるが、
そこでユーザーごとに個別のUIDを発行してくれる。
これはGoogleアカウントでのログインなどのソーシャルログインで実装した場合もUIDが発行されるので、
このIDを用いて様々な開発をアプリ内で行うことができる。

UIDを発行してくれるのもメリットの一つですね!

Authenticationには2種類存在する。
1つ目はカスタマイズが可能な、Firebase Authenticationそのまま。
2つ目はUIのカスタマイズができない、Firebase Authentication UI
どっちを使うかはその時々で選択する必要があるが、
アプリの独自性を出すだったりすると、カスタマイズ性の富んだ2つ目を利用するのがいい。

Googleアカウントでログインできるように実装

以下で扱ってます。

-Firebase, IT, アプリ開発
-,

© 2022 Yosshi Blog Powered by AFFINGER5