LoginSignup
2
5

More than 3 years have passed since last update.

Firebase Authenticationを使ってiOSアプリのログイン機能を実装する

Posted at

最近、iOSアプリにFirebase Authentication を使ってSMS認証を実装する機会がありました。
実際に利用してみた感想としては、ログイン機能新規会員登録 などの複雑を要する機能を比較的簡単に品質の高く実装できるのでおすすめ出来ると感じました。

前提

 

参考となる資料

Firebaseはドキュメントが充実しており、環境構築に関しては公式のドキュメントの手続きに沿えば基本的に問題ないと感じました。

Firebaseへ電話番号の送信を行う

[注意点] 日本の電話番号コード +81 を電話番号の先頭に付与する必要があるので、注意してください。

func sendPhoneNumber(phoneNumber: String) {

    let num = "+81" + phoneNumber
    // Firebaseへ電話番号を送信
    PhoneAuthProvider.provider().verifyPhoneNumber(num, uiDelegate: nil) { [weak self] verificationID, error in
        if let error = error { // Errorの場合はこちらへ }
        if let id = id {
            // 取得したauthVerificationIDをUserDefaultsへ保存
            UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
            // SMS画面へ遷移させる
        }
    }
}

verificationID を永続領域に保存するメリットについては、Firebaseの公式ドキュメントに記載がある通りです。

確認 ID を保存し、アプリの読み込み時に復元します。これにより、ユーザーがログインフローを完了する前にアプリが終了した場合でも(たとえば SMS アプリへの切り替え時など)、有効な確認 ID を残すことができます。

確認コードを使ってユーザーをログインさせる

以下の関数を使ってユーザの クレデンシャル を発行します。
クレデンシャル とは、ユーザのIDやPWなどのユーザ情報におけるパラメータの総称です。
クレデンシャルを生成するための関数の第一引数には電話番号を送信する際に永続領域に保存した verificationID を渡し、第二引数にはユーザが実際に入力した確認コードを渡します。

コールバックでログイン結果が返されるので、返ってきた結果によってエラー文言を出し分けたり、インジケータを表示してユーザに通信状態を伝えるとより良いと思いました。

// ユーザから入力された確認コードで
func sendSMSAuthCode(code: String) {
    // クレデンシャルの発行
    let id = userDefault.object(forKey: "authVerificationID") as? String ?? ""
    let credential = PhoneAuthProvider.provider().credential(withVerificationID: id, verificationCode: code)

    Auth.auth().signIn(with: credential) { [weak self] authResult, error in
        if let error = error {
            guard let errorCode = AuthErrorCode(rawValue: error._code) else { return }
            switch errorCode {
            case .invalidVerificationCode: print("認証コードが正しくありません")
            default: print("エラーが起きました。しばらくしてから再度お試しください。")
            }
        }
        if let _ = authResult {
            let currentUser = Auth.auth().currentUser
            currentUser?.getIDTokenForcingRefresh(true) { idToken, _ in
                guard let _ = idToken else {
                    print("トークンの取得に失敗しました")
                    return
                }
                // LOGIN SUCCESS!!!!
            }
        }
    }
}

以上で、ログイン機能は完了で🎉🎉

アプリ初回起動時にログイン状態にあるかどうかを取得したい

ログイン機能は実装できましたが、アプリ起動時にユーザにログインフローを毎回踏んで貰うのは現実的ではありません。
Firebaseは特にユーザに操作をさせず、ログイン済みかどうかのステータスを取得する事ができます。したがって、AppDelegateやユーザ情報を取得する処理の中で以下の処理を実行するとログイン済みの場合はtokenが取得出来ます。
私はとりあえず、 tokenの取得成功 サインインが必要 エラー のステータスを用意してみました。

enum FirebaseTokenCallbackResult { case success(token: String), needSignIn, error(error: Error?) }

func fetchFirebaseToken() -> Observable<FirebaseTokenCallbackResult> {
    return Observable.create { observer in
        guard let currentUser = Auth.auth().currentUser else {
            // 自分自身のアカウントが取得できなかった場合は、新規会員登録のフローを踏む
            observer.onNext(.needSignIn)
            return Disposables.create()
        }
        currentUser.getIDToken(completion: { [weak self] idToken, error in
           if let token = idToken {
                self?.firebaseToken = token
                return observer.onNext(.success(token: token))
            }
            return observer.onNext(.error(error: error))
        })
        return Disposables.create()
    }
}

注意点

AppDelegateで以下の処理を呼び出して初期化すると思いますが、以下の処理が実行されるよりも先にAuthなどの処理を呼び出してしまうとクラッシュする可能性があります。そのため、以下の処理が実行されるよりも先に currentUser などを取得しないよう注意が必要です。

FirebaseApp.configure()

以上です。

2
5
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
5