環境
- Xcode 12.3 (12C33)
- Firebase/Auth 7.4.0
- Firebase/Database 7.4.0
作成するアプリ
- 社用メールアドレスでログイン後出退勤を記録できるアプリ
今回実現する機能
- メールアドレスとパスワードでログイン
- ログイン成功した場合にはアカウントに紐づく以下の情報を返却する
- 姓
- 名
- 従業員識別キー(出退勤記録に用いる)
- ログインできるメールアドレスとパスワードはあらかじめ発行しておく
- アプリ等からの新規アカウント登録は行わない
手順
- Firebase プロジェクトを作成
- アプリを登録
- GoogleService-Info.plist をDLしプロジェクトに追加
- Firebase/Auth および Firebase/Database を Pod で導入
- Firebase のコンソール画面から Authentication を開きメールアドレスによるログインを有効化
- 社員のメールアドレスを登録
- json で Realtime Database にインポートするファイルを作成
- Firebase のコンソール画面から Realtime Database を開き作成しておいたファイルをインポート
- Realtime Database のルールの設定を変更しログインしていないアプリからデータを読み込めないようにする
- アプリ側でサインイン機能を実装する
- アプリ側でデータ取得機能を実装する
1〜4は本題ではないため以下記事を参照
5. Firebase のコンソール画面から Authentication を開きメールアドレスによるログインを有効化
- Firebase のコンソール画面から対象となるプロジェクトを選択
- 左側のメニューから「Authentication」を選択する
- Sign-in method タブを選択する
- メール/パスワードを選択する
- 「有効にする」をONにする
6. 社員のメールアドレスを登録する
- Firebase のコンソール画面の左側のメニューから「Authentication」を選択する
- Users タブを選択する
- 「ユーザーを追加」ボタンをクリックする
- メールアドレス・パスワードを記入し「ユーザーを追加」ボタンをクリックする
7. json で Realtime Database にインポートするファイルを作成
- 以下の様に json ファイルを作成
従業員データ
{
"users" : {
"(ユーザーUID1)" : {
"last_name" : "テスト",
"first_name" : "太郎",
"key" : "(従業員識別キー1)"
},
"(ユーザーUID2)" : {
"last_name" : "試験",
"first_name" : "花子",
"key" : "(従業員識別キー2)"
}
}
}
- ユーザーUID には Authentication > Users で確認できるユーザーUIDを利用する
8. Firebase のコンソール画面から Realtime Database を開き作成しておいたファイルをインポート
- Firebase のコンソール画面の左側のメニューから「Realtime Database」を選択する
- 「データ」タブを選択する
- タブ以下の画面右上の点が3つ縦に並んでいるボタンをタップする
- 「JSONをインポート」を選択する
- 7で作成したJSONファイルを選択する
- 「インポート」を選択する
- 画面に「"users"」から始まるツリー構造が追加されていることを確認する
9. Realtime Database のルールの設定を変更しログインしていないアプリからデータを読み込めないようにする
- Firebase のコンソール画面の左側のメニューから「Realtime Database」を選択する
- 「ルール」タブを選択する
- 「ルールを編集」が選択されていなければ選択する
- 以下の通り編集する
ルール
{
"rules": {
".read": false,
".write": false,
"users" : {
"$uid": {
".read": "auth.token != null && $uid === auth.uid"
}
}
}
}
- 編集後権限の設定状況に問題がなければ「公開」を選択する
ルールの設定について
- ルールはカスケード式に適用される
- 上流で true としていた場合設定されない限りは下流は全て true になる
- 下流で false に設定されるとそこから下流は全て false になる
- 今回は全体としては外部からの読み書きを禁止する
- usersのうちの
$uid
データについて以下のルールを適用する- 認証トークンが存在し、かつ
$uid
(データのキー) が認証情報のユーザーUIDと一致するデータについては読み込み権限を付与する
- 認証トークンが存在し、かつ
-
$uid
は単純に変数名なので$a
などでもOK -
auth
からログイン後の認証情報にアクセス可能 - 詳細は以下より
10. アプリ側でサインイン機能を実装する
AppDelegate
- アプリ起動時に Firebase と接続する
AppDelegate.swift
import Firebase
import UIKit
// MARK: - AppDelegate
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Firebase に接続する
FirebaseApp.configure()
return true
}
// 以下略
サインイン画面
- メールアドレスとパスワードをUIから取得し結果を completion で処理する
- サインインに成功した場合 completion に返却される
AuthDataResult?
に ユーザーUID が含まれているため、これを用いてRealtime Database からデータを取得する
SignInViewController.swift
import FirebaseAuth
import UIKit
// MARK: - SignInViewController
final class SignInViewController: UIViewController {
// 中略
@IBAction private dynamic func signInButtonTapped(_ sender: Any) {
guard
let email = self.emailTextField?.text,
let password = self.passwordTextField?.text
else {
return
}
Auth.auth().signIn(withEmail: email, password: password) { (result: AuthDataResult?, _) in
if let userId = result?.user.uid {
// サインイン成功時の処理
}
else {
// サインイン失敗時の処理
}
}
}
// 以下略
11. アプリ側でデータ取得機能を実装する
構造体の準備
- 取得したデータを入れる構造体を作成する
FBEmployee.swift
import FirebaseDatabase
import Foundation
// MARK: - FBEmployee
struct FBEmployee {
// 姓
let lastName: String
// 名
let firstName: String
// 従業員識別キー
let employeeKey: String
init(by snapshot: FirebaseDatabase.DataSnapshot) {
guard
let dictionary = snapshot.value as? NSDictionary,
let lastName = dictionary["last_name"] as? String,
let firstName = dictionary["first_name"] as? String,
let employeeKey = dictionary["key"] as? String,
else {
fatalError()
}
self.lastName = lastName
self.firstName = firstName
self.employeeKey = employeeKey
}
}
データの取得
- Realtime Database はツリー構造になっている
- Realtime Database へのリファレンスを作成し、そこから
child
ノードを探索するイメージでアクセス
- Realtime Database へのリファレンスを作成し、そこから
- ユーザーUIDがデータのキーとなるように Realtime Database を作成したため、Realtime Database の
child
にuserId
を入れてアクセスする - 今回取得するデータはほぼ変化することがなく、ログイン時に一度取得できれば良いため
observeSingleEvent(of:with:withCancel:)
を用いる-
observe(_:with:withCancel)
を用いる場合、利用しなくなったリスナーのデタッチを行う必要があるため注意
-
SignInViewController.swift
import FirebaseAuth
import FirebaseDatabase
import UIKit
// MARK: - SignInViewController
final class SignInViewController: UIViewController {
// 中略
@IBAction private dynamic func signInButtonTapped(_ sender: Any) {
guard
let email = self.emailTextField?.text,
let password = self.passwordTextField?.text
else {
return
}
Auth.auth().signIn(withEmail: email, password: password) { (result: AuthDataResult?, _) in
if let userId = result?.user.uid {
// サインイン成功時の処理
let reference = FirebaseDatabase.Database.database().reference()
reference.child("users").child(userId).observeSingleEvent(of: .value) { (snapshot: DataSnapshot) in
// データ取得成功時の処理
// 従業員データのデコード処理
let employee = FBEmployee(by: snapshot)
// ここから画面遷移等を実行する
} withCancel: { _ in
// データ取得失敗時の処理
}
}
else {
// サインイン失敗時の処理
}
}
}
// 以下略