Titanium Advent Calendar 2014、17日目の記事は「Firebaseを使ってリアルタイムサービス」と題しまして、またもやリアルタイムサービスを作成してみます。本当は今日は @infosia さんの番だったのですが、とても口に出せない密やかな理由により急遽交代することになりましたので、Ti.Nextの話題は明日の更新をお待ち下さい。
はじめに
さて、前回PubNubを使ってJSだけでリアルタイムサービスを組み込んでみましたが、いまいち知名度がないPubNubということもあって、面白そうだけど実際に使ってみるのは躊躇したという方もいらっしゃったかもしれません。その点、FirebaseはGoogleに買収されたこともあり知名度は抜群、是非試してみたいと思っていた方も多いのではないでしょうか。違いますか。そうですか。でも書き手というのはロラン・バルトが何を言おうがその点だけは常に特権的な存在なので、そういうことだとして話を進めます。
Firebaseも無料の開発者向けプランを提供しており、さまざまな機能を気軽に試すことが出来ます。Googleに吸収された後も多様なプラットフォームをサポートしており、REST APIからTitaniiumのJavaScriptだけで使うことも可能ですが、リアルタイムのメッセージ処理はちょっと苦労するかもしれません。そこで、簡単なモジュールを使って基本的なところだけ動作させることにしてみました。
モジュールはこちらで公開しています~~~~起きたら公開します需要がああれば公開します。認証など実装していない機能がまだたくさんあるので正月あたりにちょこちょこ作業してみたいとは思っています。
ええ、なぜさっくり公開しないかっていうと、作ってから気づいたのですが、他にもやっる人がいたんですよね。それからMarketplaceにだってある(有料のAndroid版、iOS版)ことにも、ここまで作業した後で気づいてしまいましたよ(GithubのはこれのiOS版ですね)。もはや無料であること、ソースコードが手に入ること、Android/iOS両方対応して同じJSで動くことにしかメリットがないんですよ。多様性は善ですが、あんまり意味ないですよねえ…
くっそ、こうなったら戦争だ!
Firebaseの(ざっくりとした)説明
気を取り直して続けます。Firebaseとその他のPub/Sub型アーキテクチャとの一番大きな違いは、全てのデータに一意のURLが付与されていることです。アカウントを登録してアプリケーションを作成すると、普通のPub/Subではトークンなどで認証してチャンネルに登録・発行するのですが、Firebaseの場合はまず最初にそのアプリケーション専用のURLが発行されます。そして、iOS、Androidなどのプラットフォームを問わず、全てのデータ操作はこのURLに対して行われます。なので、例えば他のPub/Subでよくやるような「my_room」というチャンネルを作ってそこにpublish/subscribeしたいという場合は、Firebaseの場合は「発行されたURL/my_room」へのリクエストを送信してレスポンスを取得するという作業になります。
データの保存には主に3種類の方法があります。
- 単純に文字列や数値、JSONのオブジェクトを保存する
- リスト型のデータに文字列や数値、JSONのオブジェクトを追記する
- 排他制御して文字列やJSONのオブジェクトを保存・更新する
単純なデータベースとしての利用なら1を、チャットの投稿など一意のデータを順番が保持された形式で保存したいなら2を、カウンターなどには3を利用するといいでしょう。このモジュールでは煩雑にしたくなかったので全てJSONとして処理することにしました。
モジュールの説明
さて、では実際に使ってみましょう。
Firebaseの機能を全て網羅するラッパーを作るのは骨が折れるので、Web版のJSで利用する機能からとりあえずドキュメントのガイドからデータ保存に関するものを実装してみました。単純にJSONを保存する、リストに追加する、編集する、削除する、トランザクションとして処理をするという機能があります。またデータの読み込みは同じくデータの追加、変更、削除をトリガーにして実行するようにしています。
初期設定
モジュールの使い方は非常にシンプルです。
var Firebase = require('ro.toshi.ti.app.mod.tifire');
var fb = Firebase.createFirebase({
url: 'https://YOUR_HOST.firebaseio.com/test'
});
モジュールをrequireしたら、createFirebaseで指定されたURL上への参照を作成します。上で書いたように、ベースとなるURLはFirebaseの管理画面からアプリケーションを登録した時点で発行されますので、必要に応じて「/data」などリソースの保存先を指定することもできます。
リアルタイム処理
fb.addEventListener('child_added', handleMessage);
fb.addEventListener('child_removed', handleMessage);
fb.addEventListener('child_changed', handleMessage);
fb.addEventListener('value', handleMessage);
function handleMessage(e){
Ti.API.info(e);
alert(JSON.stringify(e));
}
次に、必要なeventListenerを用意します。それぞれのeventListenerはその名の通りの動作をします。child_addedは参照先にデータが追加された時に実行されます。child_removedは参照先でデータが削除された時、child_changedは変更が実行された時に呼び出されます。valueは内容を問わずなんらかの変更が実行された際に呼び出され、直近のデータを返します。
取得したデータには全て_idというプロパティを追加してみました。これがサーバ上に保存された一意のIDなので、URLとしてアクセスすることも出来ます。管理画面にログインしていたら、createFirebaseしたときのURLにこの_idの値を追加して「URL/_idの値」でアクセスしてみましょう。
しましたね?では先に進みます。
データの保存
function publish(e) {
fb.set({name: Ti.Platform.osname, message: "Yo!"});
}
データの保存には三種類のメソッドが用意されています。setは単純に参照先にJSONオブジェクトを保存します。先に保存されたデータは上書きされます。
function publish(e) {
fb.push({name: Ti.Platform.osname, message: "Yo!"});
}
setのかわりにpushを使うと、データはリスト型になるのでsetのように上書きされずに保存された順番を保持したまま配列のように扱うことができるようになります。
三つ目のtransactionは眠いのでそのうちやります。
Firebaseの仕様で最初に参照先URLに接続すると過去のデータを一定数返すのでイベントが発生するようです。
その他
なんだか楽しそうなので、Skypeみたいな使い方を想定してオフラインになったりオンラインになったりする機能を追加してみました。
fb.goOffline(); //オフラインになる
fb.goOnline(); //オンラインになる
いずれもアプリ内の全てのFirebaseへの通信がオフラインまたはオンラインになるので注意してください。
まとめ
いかがでしたか?Firebaseには他にも優れた(そして簡単に扱える)セキュリティの仕組みが用意されています。例えば閲覧権限からデータのバリデーションまで、JSON形式で指定するだけで個々のデータのセキュリティモデルを記述することができます。いずれその辺りまで対応させていければと思いますので、需要があればお知らせください。っていうか既存のがあるし…
ちょっと首吊ってくるんで明日は @infosia さんです。