やってみた

Firebaseを使ってみた

皆さま、こんにちは!
今回の記事では、私が実際にFirebaseを使ってみたその内容を共有いたします。

Firebaseとは何か?

Firebaseは、モバイルアプリのためのバックエンドサービスです。
公式サイト: https://firebase.google.com/

バックエンドサービスとは、モバイルアプリの構築に必要なサーバーサイドの機能(データベース、プッシュ通知の配信、ユーザー管理など)を提供しているサービスのことです。
BaaS(Backend as a Service)と呼ばれることも多く、流行のきざしを見せています。

BaaSでは、Facebook社が買収した「Parse」がもっとも有名で、多くの利用者がいました。
しかし、Parse は突如サービスの終了を発表し、今年ついにサービス終了してしまいました。

FirebaseはParseの受け皿として注目されていますが、それだけではなく多くのパワフルな機能を備えています。今回は、そのパワフルな機能の中から一部をご紹介いたします。

Firebaseの機能

Firebaseの機能(プロダクト)の一覧は、公式サイトに掲載されています。
機能一覧:https://firebase.google.com/products/

今回は、Firebase が持つ機能の中から一部をピックアップしてご紹介いたします。

1. Realtime Database(リアルタイムデータベース)
アプリのデータを瞬時に同期することができる、NoSQL型のデータベースです。

・ Web / iOS / Android など、さまざまなデバイスでデータを同期することができます。
・ 接続しているすべてのデバイスが、わずかミリ秒レベルの遅延でリアルタイムにデータを同期することができます。
・ モバイルならではの通信状態が不安定なケースの対策も完備されています。オフラインになるとデータの同期を停止し、オンラインに復帰すると自動的にデータ同期を再開するようになっています。

リアルタイムデータベースは、私たちエンジニアが抱えている「デバイス間の差異やローカルDBとクラウドDBのデータ同期」といった悩みを解き放ってくれるのです。

2. Firebase Authentication(認証)
アプリに認証システムを組み込むことができます。

・ Googleログインの開発チームによって構築されており、Googleのセキュリティを活用した安全な認証システムを利用することができます。
・アプリの認証システム全体を、わずか10行弱のコードでセットアップすることができます。
・ Google / Twitter / Facebook / GitHub などのSNS認証に対応しており、ログインの柔軟性を高めることができます。

認証用のユーザーデータはFirebase上に保存され、エンジニアは新規にログインシステムを構築する必要はありません。Firebaseの認証を呼び出すだけで、アプリ上にログインシステムを組み込むことができるのです。

3. Firebase Cloud Messaging(通知)
通知(プッシュ通知)を配信することができます。

・ プッシュ通知の配信を自社のサーバーで行なうと、どうしても負荷が気になりますが、Cloud Messagingを活用すれば実際の配信を代行してくれます。
・ かつ、配信条件を設定することで、特定のターゲットのみに絞って配信することが可能になります。
・ もちろん、通知内容をカスタマイズすることもできます。サウンドの設定から通知経由によるコンバージョンのトラッキング計測まで、可能です。

—–
以上、3つのプロダクトを紹介しました。
エンジニアがシステム基盤の構築から解き放たれ、サービスの根幹となるアプリケーションの開発に注力できるようにしてくれるのがFirebaseなのです。
試さない手はないですね!

Firebase のセットアップ


では、実際にFirebaseを利用するための手順を振り返っていきましょう。
まず、Firebaseを利用するには、利用開始手続きをする必要があります。
※ Google アカウントが必要になります。アカウントをお持ちでない方は、先にアカウント作成を行なってください。

1. 無料プランを契約しよう
Firebaseの料金表ページを表示してください。
料金表: https://firebase.google.com/pricing/

Spark(無料)プランを選択して、Firebaseの利用を開始します。
(注意:本記事では個人レベルの検証利用を想定しておりますので、無料プランを契約します)

2. プロジェクトを作成しよう
利用開始手続きを行なうと、プロジェクト選択ページに移動します。
プロジェクトを作成しましょう。

特に難しいところはなく、画面の流れに沿って必要事項を入力するだけでプロジェクトを作成することができます。


(注意:筆者は既に2つのFirebaseプロジェクトを作成しているため、警告が出力されております。通常、初回利用であれば、警告は表示されません)

3. モバイルアプリをセットアップしよう
プロジェクトを作成すると、Firebaseプロジェクトのダッシュボードに遷移します。
ここで、デバイスを選択して接続するモバイルアプリをセットアップしましょう。

セットアップも特に難しいところはなく、画面の案内に沿って進むだけで行なうことができます。
大まかな流れとしては、以下の通りです。

・ 1. SDKのインストール(例:iOSであれば、Podfileに記載しpod installを行なうだけで可能です)
・ 2. コンフィグファイルをアプリに設置します(APIKeyなどが記載されていますので、外部には公開しないようにしましょう)
・ 3. アプリの起動時に、コンフィグを読み込んでFirebaseのセットアップするコードを記述します。


(注意: モバイルアプリのセットアップは、iOS / Android / Webでそれぞれ行なう必要があります)

Realtime database と接続してみよう (iOS & Node.js 編)

今回は、Firebaseの機能の中からリアルタイムデータベースを例としてとりあげます。
モバイルエンド側からはiOS、サーバーサイド側からはNode.jsを選択し、実際にリアルタイムデータベースに接続してみました。

私の感想は以下の通りです。
「たったこれだけでリアルタイム同期されたデータベースが使えるの!」
Firebaseが便利であることを体感していただくために、接続の際に使用したサンプルコードをご紹介いたします。

(注意: 以下はサンプルのため、エラー検知やトランザクション処理を簡略化(および割愛)しております)

1. iOS
1.1 (Firebase iOS SDK) セットアップ – AppDelegate.swift

import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {        
        // アプリの起動時にFirebaseをセットアップする
        FirebaseApp.configure()
        return true
    }
}

1.2 (Firebase iOS SDK) データを保存する

// SEE: https://firebase.google.com/docs/database/ios/read-and-write
let realtimeDb = Database.database().reference()

/**
 * データを保存する
 * (データが存在しない場合は新規に作成します)
 *
 * リアルタイムデータベースでは、ディレクトリ階層をスラッシュで表現します
 * 以下の例では「testディレクトリの10001レコード」に保存します
 */
realtimeDb.child("test/10001").setValue([
    "username": "test",
    "email" : "test@example.com"
])

1.3 (Firebase iOS SDK) エラーを検知する

/**
 * エラーを検知する
 *
 * オーバーロードによって、同じ関数でも様々な呼び出し方が存在します
 * 以下の例では、クロージャを呼び出すことでエラー検知を追加しています
 */
realtimeDb.child("test/10001").setValue(
    ["username": "test", "email" : "test@example.com"],
    withCompletionBlock: { (error: Error?, ref: DatabaseReference) in
        if let _error = error {
            print("エラーが発生しました")
            print(_error)
        }
    }
)

1.4 (Firebase iOS SDK) データを取得する

/**
 * データを取得する
 * (普通にデータベースをSELECTする使い方です)
 *
 * 以下の例では「testディレクトリの10001レコード」を取得します
 */
realtimeDb.child("test/10001").observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
    print("データを取得しました")
    print(snapshot.value)
})

1.5 (Firebase iOS SDK) データの変更を監視する

/**
 * データの変更を監視する
 * (変更が発生するたびにコールされます)
 *
 * 以下の例では「testディレクトリ」配下を監視します
 * 何らかの変更があるとクロージャがコールされます
 *
 * イベントが発火するタイミングはDataEventTypeで変更することができます
 * SEE: https://firebase.google.com/docs/reference/ios/firebasedatabase/api/reference/Enums/FIRDataEventType
 */
realtimeDb.child("test/").observe(DataEventType.value, with: { (snapshot) in
    print("データが変更されました")
    print(snapshot.value)
})

1.6 (Firebase iOS SDK) トランザクションを利用する

/**
 * トランザクションを利用する
 * SEE: https://firebase.google.com/docs/database/ios/save-data?hl=ja
 *
 * 通常のRDBのように、トランザクションを発行することが可能です
 * 詳細は公式ドキュメントを参照してください
 * 以下はrunTransactionBlockを使用した場合の例です
 */
realtimeDb.child("test/10001").runTransactionBlock(
    { (data: MutableData) -> TransactionResult in
        // TransactionResult.abort()によって、トランザクションを破棄することができます
        if let _username = (data.value as? [String: Any])?["username"] as? String {
            if _username == "test" {
                print("testさんの場合はトランザクションをコミットしません")
                return TransactionResult.abort()
            }
        }
                
        // test/10001の内容を書き換えます
        data.value = ["username": "test2", "email" : "test2@example.com"]

        /**
         * TransactionResult.successによって、トランザクションを成功とみなします
         * 但し、トランザクションは拒否されることもあります
         * 以下、公式ドキュメントからの引用です
         *
         * 引用: サーバーは、この初期値をその現在の値と比較し、値が一致した場合はトランザクションを受け入れ、そうでない場合は拒否します。
         */
        return TransactionResult.success(withValue: data)
    }, andCompletionBlock: { (error: Error?, committed: Bool, snapshot: DataSnapshot?) in
        // エラーを検知することができます
        if let _error = error {
            print("エラーが発生しました")
            print(_error)
        } else if !committed {
            // TransactionResult.abort()した場合はここにきます
            print("変更しませんでした")
        } else {
            print("成功しました")
        }
    }
)

2. Node.js
2.1 (Firebase Admin Node.js SDK) セットアップ

const admin = require("firebase-admin");

/**
 * セットアップ
 * SEE: https://firebase.google.com/docs/database/admin/start
 **/

// ダウンロードしたアカウント情報のJSONファイルを指定してください
const serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  // データベース名を指定してください
  databaseURL: "https://[DATABASE_NAME].firebaseio.com"
});

2.2 (Firebase Admin Node.js SDK) データを保存する

// SEE: https://firebase.google.com/docs/database/admin/save-data
const db = admin.database();

/**
 * データを保存する
 * (データが存在しない場合は新規に作成します)
 *
 * 「testディレクトリの10001レコード」に設定した値を保存します
 */ 
db.ref("test/10001").set({
    'id': 10001,
    'name': 'testName'
}, function (error) {
  if (error) {
    console.log("失敗しました");
    console.log(error);
  } else {
    console.log("成功しました");
  }
});

2.3 (Firebase Admin Node.js SDK) データを取得する

/**
 * データを取得する
 * (普通にデータベースをSELECTする使い方です)
 * SEE: https://firebase.google.com/docs/database/admin/retrieve-data
 *
 * 以下の例では「testディレクトリの10001レコード」を取得します
 */
db.ref("test/10001").once("value", function(snapshot) {
  console.log(snapshot.val());
}, function (error) {
  if (error) {
    console.log("失敗しました");
    console.log(error);
  } else {
    console.log("成功しました");
  }
});

2.4 (Firebase Admin Node.js SDK) データの変更を監視する

/**
 * データの変更を監視する
 * (データの変更が発生するたびにコールされます)
 *
 * 以下の例では「testディレクトリ」配下を監視します
 * 何らかの変更があるとコールバックがコールされます
 * イベントが発火するタイミングは第一引数で変更可能です
 * SEE: https://firebase.google.com/docs/database/admin/retrieve-data?hl=ja
 */
db.ref("test/").on("value", function(snapshot) {
  console.log(snapshot.val());
}, function (error) {
  if (error) {
    console.log("失敗しました");
    console.log(error);
  } else {
    console.log("成功しました");
  }
});

2.5 (Firebase Admin Node.js SDK) トランザクションを利用する

/**
 * トランザクションを利用する
 * SEE: https://firebase.google.com/docs/database/ios/save-data?hl=ja
 *
 * 通常のRDBのように、トランザクションを発行することが可能です
 * 詳細は公式ドキュメントを参照してください
 */
db.ref("test/10001").transaction(function(currentData) {
  // 例:新規データの場合のみコミットする
  if (currentData === null) {
    // Success the transaction.
    return { 'id': 10001, 'name': 'testName' };
  } else {
    console.log('既にデータが存在します');
     // Abort the transaction.
    return;
  }
}, function(error, committed, snapshot) {
  if (error) {
    console.log("エラーが発生しました");
    console.log(error);
  } else if (!committed) {
    console.log("変更しませんでした");
  } else {
    console.log("成功しました");
  }
});

まとめ

Firebaseを活用すると、モバイルアプリの構築に必要なサーバーサイドの機能を、手軽に組み込むことができます。

・ 作りたいアプリサービスがあるけれど、私はアプリエンジニアなのでサーバーサイドプログラミングは詳しくありません。
・ サーバーサイドの実装はクラウドサービスに任せて、アプリケーションの開発に集中したいです。

そんな悩みに解答できる時代が、到来のきざしを見せています。
Firebaseを活用すれば、自前でサーバーを用意しない「サーバーレスアーキテクチャによるサービス構築」も夢ではありません。

ここまでお読みいただきまして、ありがとうございました。

次回は、リモートワークを活用している社員による「リモートあるある」ネタをお届けします。
お楽しみに!

※当記事は、2017年8月8日時点の情報を元に作成しております。

引用元・出典
Facebook傘下のParse、サービス終了へ
BaaSとは
【3分で読める!】サーバレスアーキテクチャって何?エンジニアが憧れるクラウドを活かしたシステムのご紹介

LINEで送る
Pocket

この記事を書いた人・プロフィール
ササテン
ニックネーム: ササテン

前職の会社が倒産したことを機に入社。初めて業務で使用した言語は Perlでした。Perl の精神である「There's More Than One Way To Do It.(やり方は何通りもある)」が気に入っていて、自由度の高いプログラミングスタイルが好きです。

趣味:ゲーム、将棋、麻雀