Swift Firebase Google Authenticationを導入


Firebase側の設定

ログイン方法 > Googleを有効にしておく。

f:id:letitride:20190812090716p:plain

プロジェクトの公開名は認証画面で表示されるアプリ名となる。

アプリの基本設定

podのインストール

pod 'Firebase'
pod 'Firebase/Auth'
pod 'GoogleSignIn'
$ pod install
$ open FirebaseChat.xcworkspace
GoogleService-Info.plistの設置

FirebaseからダウンロードしたGoogleService-Info.plistをアプリケーションフォルダに設置する。

f:id:letitride:20190812091045p:plain

仮に紛失した場合等は、プロジェクトの設定より再度DLできる。

f:id:letitride:20190812091223p:plain

URL Schemeの設

GoogleService-Info.plistのREVERSED_CLIENT_IDに設定されているcom.google〜から始まる値をURL Typesに設定する。

プロジェクトファイルから、TARGETS > infoタブを選択

f:id:letitride:20190812091820p:plain

f:id:letitride:20190812091833p:plain

下段にあるURL TypesのURL SchemesにREVERSED_CLIENT_IDを入力する。

f:id:letitride:20190812092039p:plain

AppDelegateの設定
import GoogleSignIn
import Firebase

Google認証からのURL open時の処理を記述

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let handle = GIDSignIn.sharedInstance()?.handle(url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation])
    return handle!
}

Firebase SDKの初期化。didFinishLaunchingWithOptionsで書くのが慣例みたい。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()
    GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
    return true
}

ログインを行うViewControllerでの記述

import Firebase
import GoogleSignIn
Googleログインボタンの設置

Googleログインボタンの設置。GoogleSignInパッケージの中にGIDSignInButtonというViewが入っている。

func createGoogleSigninButton(){
    let googleButton = GIDSignInButton ()
    googleButton.frame = CGRect(x: 20, y: self.view.frame.height/2-30, width: self.view.frame.width-40, height: 60)
    self.view.addSubview(googleButton)
}
delegateの記述

GIDSignInDelegate, GIDSignInUIDelegateの実装をGIDSignInに渡す必要がある。公式ではGIDSignInDelegateはAppDelegateで実装を記述していたが、ログインを行うViewControllerで記述しても問題がなかった。

 override func viewDidLoad() {
     super.viewDidLoad()
    GIDSignIn.sharedInstance()?.delegate = self
    GIDSignIn.sharedInstance()?.uiDelegate = self
    createGoogleSigninButton()
}

認証結果コールバックの記述

extension LoginViewController: GIDSignInDelegate, GIDSignInUIDelegate{
    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
        if let error = error {
            print(error.localizedDescription)
            return
        }
        guard let authentication = user.authentication else { return }
        
        //credentialはFirebase登録/ログイン時に必要
        let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                       accessToken: authentication.accessToken)
        //Firebase Baas側に認証(初回時はレコードを作成)
        Auth.auth().signIn(with: credential) { (authResult, error) in
        }
        
        //ユーザ情報へのアクセス
        Auth.auth().addStateDidChangeListener { (auth, user) in
            if let currentUser = auth.currentUser{
                print(currentUser.uid)
                print(currentUser.displayName)
                print(currentUser.email)
                print(currentUser.photoURL)
                
                //アドレス確認メールの送信
                Auth.auth().currentUser?.sendEmailVerification { (error) in
                }
            }
        }
    }
}

ユーザ情報へのアクセスはGoogleの認証結果、signInやuserからでもアクセスできるが、mBaasのデータを参照したほうがお行儀がいいと思う。

流れとしては、

  1. GoogleのOAuthプロバイダで認証
  2. credentialでFirebaseの認証を取得。初回時はユーザデータの作成。この時、ユーザのprofileデータもFirebase側に作成される。
  3. 認証を得たFirebaseオブジェクトでユーザ情報を取得
Firebase Authenticationのサイレントログイン
GIDSignIn.sharedInstance().signIn()

delegateを設定する前に呼ぶとエラーになる。

 override func viewDidLoad() {
     super.viewDidLoad()
     GIDSignIn.sharedInstance().signIn()    //error
    GIDSignIn.sharedInstance()?.delegate = self
    GIDSignIn.sharedInstance()?.uiDelegate = self
    createGoogleSigninButton()
}
Firebase Authenticationのログアウト
try? Auth.auth().signOut()
Googleログインボタンの表示制御

ログイン状態によってボタンの表示・非表示を行う場合は以下のようにすればよい。

override func viewDidLoad() {
    super.viewDidLoad()
    GIDSignIn.sharedInstance()?.delegate = self
    GIDSignIn.sharedInstance()?.uiDelegate = self

    Auth.auth().addStateDidChangeListener { (auth, user) in
        if auth.currentUser == nil{
            self.createGoogleSigninButton()
        }else{
            print(auth.currentUser?.uid)
            print(auth.currentUser?.displayName)
        }
    }
}