LoginSignup
2
2

More than 1 year has passed since last update.

[Swift] FirebaseAuthenticationを使用したユーザー登録機能メモ

Last updated at Posted at 2021-09-09

はじめに

iOSアプリ開発の学習で、FirebaseAuthenticationを使用してログイン機能を実装しました。

実装した機能の一部抜粋ではありますが、備忘録的に残しておきます。

この記事に書いたこと

  • createUserメソッドを使用したユーザー登録機能
  • signInメソッドを使用したユーザーログイン機能
  • sendEmailVerificationメソッドを使用した確認メール送信機能

※Firebaseの導入については本記事では触れません。

実装

UserAuthManager

ユーザーの認証を受け持つクラスを作成します。
外部サービスのimportをあちこちに記述するのはあまりよろしくないので、
import FirebaseAuth等の記述はこのファイルのみで完結できるように設計します。

UserAuthManager.swift
import Foundation
import FirebaseAuth
import Firebase

final class UserAuthManager {
    static let shared: UserAuthManager = .init()
    private init(){}

// ~~~~~ 処理を実装 ~~~~~ 
}

UserAuthError(エラー型の用意)

サーバーからエラーが返ってくるケースに備えて作成しておきます。

UserAuthManager.swift
enum UserAuthError: Error, LocalizedError {

    case createUserError(errorMessage: String)
    case userLoginError(errorMessage: String)
    case sendEmailVerificationError(errorMessage: String)
    case checkEmailVerification(errorMessage: String)

    var errorDescription: String? {
        switch self {
        case .createUserError(let message):
            return "ユーザー作成エラー:\(message)"
        case .userLoginError(let message):
            return "ログインエラー:\(message)"
        case .sendEmailVerificationError(let message):
            return "確認メール送信エラー:\(message)"
        case .checkEmailVerification(let message):
            return "メールアドレス検証エラー:\(message)"
        }
    }
}

createUser(ユーザーの新規作成)

UserAuthManager.swift
func createUser(email: String, password: String, completion: @escaping (Result<Bool, UserAuthError>) -> Void) {
    Auth.auth().createUser(withEmail: email, password: password) { authResult, error in

        if let error = error {
            completion(.failure(UserAuthError.createUserError(errorMessage: error.localizedDescription)))
            return
        }

        guard authResult != nil else {
            completion(.failure(UserAuthError.createUserError(errorMessage: "入力された情報では登録できませんでした")))
            return
        }
        completion(.success(true))
    }
}

Result

クロージャーでResult<Bool, UserAuthError>を返していますが、これはユーザー登録出来たかどうか以外特に渡したいパラメータも無かったので成功時Bool型にしています。

ただ失敗時は基本的にError型が返されるのでfalseが使われることがないのはちょっと気になっているところです。
もっと良い書き方がありそう。

error

サーバーから返ってきた結果について何パターンか処理を分けます。

  1. errorが返ってきた場合(errorがnilでない)
    この場合はResultでUserAuthErrorを返します。この時、呼び出し元にエラーの原因(通信状況?アドレス被り?など)も知らせる必要があるのでUserAuthError.createUserError(errorMessage: error.localizedDescription)としてサーバーから受け取ったエラーメッセージも付けて返します。
  2. errorは無いがauthResultも無い場合
    errorが返ってきてないのにも関わらずauthResultが無い場合は、サーバー側も想定外のエラーとなるので、アプリ側でエラーメッセージを用意してUserAuthErrorを返します。
  3. エラーもなくauthResultがある場合
    登録成功しているので.success(true)を返します。

signIn(ログイン)

UserAuthManager.swift
func login(email: String, password: String, completion: @escaping (Result<Bool, UserAuthError>) -> Void) {
    Auth.auth().signIn(withEmail: email, password: password) { authResult, error in

        if let error = error {
            completion(.failure(UserAuthError.userLoginError(errorMessage: error.localizedDescription)))
        }

        guard authResult != nil else {
            completion(.failure(UserAuthError.userLoginError(errorMessage: "入力された情報ではログインできませんでした")))
            return
        }
        completion(.success(true))
    }
}

やっていることはcreateUserとほぼ同じです。

sendEmailVerification(メールアドレスの確認)

不正なメールアドレスの使用を防ぐため、登録したメールアドレスに確認用のメールを送信する機能です。
受けとったメールアドレスのリンクをタップすることで、currentUser.isEmailVerifiedというパラメータがfalseからtrueに変わります。trueであれば確認済みの状態ということになります。

UserAuthManager.swift
func sendEmailVerification(completion: @escaping (Result<Bool, UserAuthError>) -> Void) {
    guard let user = Auth.auth().currentUser else { return }
    user.sendEmailVerification { error in
        if let error = error {
            completion(.failure(UserAuthError.sendEmailVerificationError(errorMessage: error.localizedDescription)))
        }
        else {
            completion(.success(true))
        }
    }
}

func checkEmailVerificationResult(completion: @escaping (Result<Bool, UserAuthError>) -> Void) {
    guard let user = Auth.auth().currentUser else { return }

    user.reload { error in
        if let error = error {
            completion(.failure(UserAuthError.checkEmailVerification(errorMessage: error.localizedDescription)))
        }
        else {
            completion(.success(user.isEmailVerified))
        }
    }
}

関数を2つ用意しています。

sendEmailVerification()はcurrentuserのメールアドレスに確認用のメールを送信するためのものです。
エラーなく送信完了の場合はtrueを返し、エラーがある場合はUserAuthErrorを返します。

checkEmailVerificationResult()はメールアドレスの確認が終了しているか否かをチェックするためのものです。
エラーが返された場合(チェック自体が出来なかった場合)はUserAuthErrorを返し、エラーがなければisEmailVerifiedのBool値を返します。

補足

sendEmailVerificationはcurrentUserが持つメソッドなので、登録完了したユーザーに対して行うことになるという点に注意

Routing(おまけ)

アプリの起動経路の道中でユーザのログイン状態によって画面遷移先を変えます。
細かいところは省略していますが、userLoginCheckという関数を用意してcurrentuserを取得してログイン状態のチェック、及びメール確認の有無を見ています。

// Routerなどの画面遷移処理を受け持つクラス
func showRoot() {
    UserAuthManager.shared.userLoginCheck { result in
        switch result {
        case .success(let user): // ログインしている場合
            if user.emailVerified {
                // さらにメールの確認も済んでいる場合は、ログイン後のトップページ等へ
            }
            else {
                // メールの確認が済んでいない場合は、メールの確認処理を受け持つ画面へ
            }   
        case .failure: // ログインしていない場合
            // ログイン・新規登録の画面へ
        }
    }
}
UserAuthManager.swift
// UserAuthManagerクラスに関数を用意
func userLoginCheck(completion: @escaping (Result<User, UserAuthError>) -> Void) {
    let user = Auth.auth().currentUser

    guard let userEmail = user?.email,
          let userEmailVerified = user?.isEmailVerified else {
        return completion(.failure(UserAuthError.loginFailure))
    }
    completion(.success(User(emailAddress: userEmail, emailVerified: userEmailVerified)))
}

終わりに

このようにユーザー認証用のクラスを用意しておくことで、ViewからUserAuthManager.shared.hogehoge { クロージャー }という形で呼び出せます。
FirebaseAuthは個人アプリ開発などでもお世話になると思いますので、今後さらに理解を深めていければと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2