去年の記事、下書きのままだったので今更ですが公開します。モバイルアプリケーション用データベースRealmのミートアップです。
- 沢山の企業に採用されています
- みなさんが使っているiPhoneの中には少なくとも1つはRealmを使ったアプリが入っているはずです
- RealmのDBエンジンはモバイルデバイスに最適化されるように1から開発されました
- 大きな特徴であるオブジェクトの自動更新 "Live Object" という機能を持っていたり、モバイルアプリを作る上で便利な機能が整っています。
- Live ObjectやReactive、オープンソースであるということは大きな特徴ですが、これからその特徴の中でもDBの部分の特徴であるLiveObjectを使って、Reactiveなモバイルアプリをどのように作っていくかを少し詳しくご説明します。
- LiveObjectを使うとデータが勝手に更新されるので、データを取り直す必要がなくなります。
- 上手く使うことによってモバイルアプリケーションをスマートに開発できるようになります
- Realmの重要な代表的なAPIはTransactions, Objects, Queries, Notificationsです
- 最初はObjectsのAPIです
- Realmを使うにはRealmに保存するためのオブジェクトが必要になります。
- 通常通りクラスを定義するだけです。SwiftならRealmの"Object"を継承する、Javaなら"RealmObject"インターフェースを継承する必要がありますがそれだけです。
- 中間のモデルファイルやDBの定義は必要ありません。ミドルウェアが不要で直接オブジェクトを使えるということが重要な特徴になります。
- このRealmのモデルには自由にプロパティを定義できて、Realmに保存されます
- メソッドを定義することも出来ます
- SQLのようなJoinという概念はないですが、Relationshipsを定義することは出来ます。
- 関連を直接定義することができ、関連が保存されるので、Joinのようなハイコストな処理ではなく、パフォーマンスを気にせず関連するオブジェクトを取得することが出来ます。
- 作成したオブジェクトを保存して、それを利用するにはQueryを利用します
- Queryを使うとResultというオブジェクトを取得でき、そのなかから取り出すことが出来ます。
- 関連するオブジェクトをプロパティから取得できます。
- 1対多ならListとして取得できます。
- 逆方向の関連もプロパティとして取得できます。
- Realmはできるだけメモリを使わないようにします。Queryを実行してもオブジェクトや、関連オブジェクトにアクセスするまでは実際に取得しません。
- Queryを書くのは非常に簡単です。
- Queryの結果は、変更があった場合自動的に更新されます。2歳以下の犬を取得して得た結果があるとします。2歳以下の犬がその後追加されたら、再度検索を行う必要はありません。自動で変更が反映され、変更があったことを通知で取得できますのでそのまま画面を更新すれば良いです。そうした簡単な方法でアプリケーションを作ることが出来ます。
- パフォーマンスのためにRealmのデータをキャッシュするようなことは必要ありません。Realmに任せるだけで全て最適化されて実行されます。
- 別のスレッドから変更される可能性がありますがどうすればよいでしょうか。Queryの結果は自動で更新されるので、通知を受けて画面を更新すれば良いです。
- 通知にブロックを登録して、そのブロックの中で画面を更新したりします。
- 何が追加され、何が削除され、、と言った情報がわたってくるので、画面を更新する際にアニメーションさせることも可能です。
- アプリケーションで犬の画像を共有するInstagramのようなものを作るとします。
- ユーザーが追加したり削除したりしてデータが変更される場合があります。データの変更はTransactionsというAPIを使って行います。
- データの更新はオブジェクトのプロパティを更新するだけです。
- Realmは内部的にはメモリマップして動いているのでプロパティの変更は即座にディスクに反映されます。Transactionを使うことで変更をロールバックしたりまとめて反映させることが可能になっています。
- Transactionをつかうことで変更に一貫性を持って、たとえば別スレッドで未コミットの状態のものが見えたりしないようになっています。Transaction中の中途半端な変更はほかからは見えないようになっています。
- 最初はObjectsのAPIです
- ここまでが基本的な機能のご紹介になります
- 基本的には高速で使いやすいデータベースになっています。
- Java, Swift, Objective-C, ReactNative, Xamarin 5つのプラットフォームに対応しています
- ここからは、データベースの他に開発者の方が課題だと考えていることについてのソリューションをご紹介します。データの同期に関するソリューションです。
- Realm Mobile Platform
- データを同期しようとすると複雑で神経をすり減らすのではないでしょうか
- 表面に出ている部分は簡単でAPIを叩いてデータを取得して保存するだけ。でも実際には隠れている部分がすごく大変で、リクエストが失敗したらどうするのか、度のタイミングでエラーが起きるかだけでもたくさんあるし、どうやり直すかの場合分けも考えているととても複雑になります。
- 同期がうまく行ったとしても実はそのときには別のユーザーがサーバーのデータを更新しているかもしれません。そうした場合もデータを正しく反映することはとても難しいです。
- Realmを使った場合は全てのデータは自動的に同期されるようになります。デバイスがネットワークにつながっている間は。
- クライアントとサーバーで同じオブジェクトが使えて同じデータが存在して、同期された状態になります。
- Realmはクライアントとサーバーで双方向に通信しています。リアルタイムに最新の状態が同期されるようになっています。もし同じものを各デバイスで変更して衝突した場合も、予め決められたルールで解決されてデータが反映されます。
- 同期の通信は最小限に抑えられているのでCPU消費もネットワークの消費も非常に低く抑えられています。
- 今までと同じようにデータの変更をローカルのデータベースに書き込めばいいだけになります。
- 面倒な部分はRealmによって自動で行われます。
- 唯一単体でRealm Mobile Databaseを使っていて違うことは、バックグラウンドのスレッドでの変更をUIに変更しなければならないのと同じ感覚で、他のデバイスで置きた変更が他のデバイスに通知されるので、他のデバイスで置きた変更もUIに反映する機能を備えていなければなりません。
- JSONから変換する処理も不要になりますし、APIにアクセスする必要もなくなります。データの転送を全てRealmに任せることが出来ます。
- 差分だけをやり取りするので通信量は少なく済みます。
- よくあるネットワーク通信を使ったモバイルアプリケーションはJSONのシリアライズなどとてもたくさんのことをクライアント側で行う必要があって、スパゲッティのようになってしまいがちで、メンテナンスが難しくなることが容易に起こります。
- RealmMobilePlatformを使うと、同期が自動でRealmで行われます。
- もし必要なら既存のAPIとRealm Mobile Platformを通信させながら、クライアントはRealmを使ってシンプルにということが出来るようになっています。
- サーバー側もクライアントサイドと同様に簡単にRealmを使うことが出来ます。
- サーバーサイドのRealmはNode.jsを使います。ReactNativeを使っていたらそれと使い方は全く同じです。
- 基本的な同期の機能の他にほかにも多くの機能を提供しています。ユーザー認証、水平スケーリングなど。幾つかの機能はこのあとデモします。
- Realmはサーバーサイドとクライアントで自動的な同期を提供する上に、Enterprises機能では水平方向のスケーリングやディザスタリカバリのためのバックアップも提供しています。
- Realmのお絵かきアプリを使って日本語を教えるデモをします。
- 線を書くと座標データがRealmに保存され、Realm Mobile Platformを介してそれが別の端末に同期されます。即座に反映されます。
- これが、ローカルのDBに書き込むようにするだけで実現できるのがとても重要な事です。
- Realm mobile platformのDemoはrealmのサイトからダウンロードできます。
- Realm Mobile Platform Realm mobile platformは複数のエディションが有ります
- 無料
- 同期機能を使う
- 有料: 2ヶ月トライアル
- サーバーサイドで処理が書ける
- We're hiring
- 沢山ポジションが有るわけではない(とくにここに多いと思われるiOSエンジニアのポジションはない)が、リモートで働いている人もたくさんいます。
- リモートワークに興味が有る方も話を聞きに来ていただければと思います
- Realm Jobs | Work at Realm
ご清聴ありがとうございました。何かご質問の有る方はいらっしゃいますか?
Q: メモリのコピーが起きないとおっしゃっていたが、ファイルから読み込むときはメモリ領域を確保しなくてはいけないと思うが、二回目Queryを実行する時にキャッシュされているからコピーが発生しないのでしょうか?
A: 仕組みとして、Realmのファイルとメモリはマップされていて、RealmオブジェクトはRealmのディスクからデータを実体化したものではなく、メモリマップされたポインタを持っているものになります。オブジェクトへのアクセスはRealmからコピーされたデータへのアクセスではなくて、メモリマップされたRealmのデータに直接アクセスしているような形になります。 Realmインスタンスを作ったときはメモリを利用するが、Realmに保存されたら、メモリは使わないことになります。 メモリマップされたものはキャッシュメモリとは別の管理になります。
Q: サーバーサイドはnode.jsしか使えないということだが、Swiftも使えないでしょうか
A: 現在は出来ないです。 RealmSwiftはObjective-Cに依存しています。Objective-Cは現在Linux環境では利用できません。 またRealmのCoreはC++です。SwiftからC++をダイレクトに扱う方法は提供されていないのでObjective-Cを挟んで利用している。 今のところServerSideのRealmはその2つの理由でSwiftを利用できません。 RealmはPureSwiftで書くというプランになっているので遠くない将来実現できると思います。そうしたいと思っています。
Q: お絵かきのアプリがありましたがどのような形でデータを保存しているのでしょうか
A: とてもいい質問ですね。 結論から言うと、色と、どこからスタートして通った座標と終わった座標をDBのSchemeとして持っています。そのようにデータを持っている理由は二つのデバイスを同期してもあとから来たデータを追加するだけで良いようにするためです。 ストロークと、いつ、という情報だけ持っていれば、基本的にはあとから来たものを追加していくだけになるので、そのようなデータの持ち方をしています。
Q: Swiftは基本的に値型がメインだと思うが、RealmObjectは参照型である。その点は相性が悪いということはないでしょうか
A: SwiftはValueTypeを推奨しているのはおっしゃるとおりです Realmはデータベースを使う以上は更新される度に引きなおすこともありですが、Live Objectというコンセプトがあるのでそのような方法を取っています。 将来的なプラントしてはバリュータイプを扱えるようにするという検討もしています。
Q: サーバーサイドのDBと同期できるということで、PostgresSQLを利用できるということだが、ほかのRDBは利用できるようになりますか?
A: はい。ほかのDB対応予定もあります。MySQLなどもプランに有ります。
Q: サーバーサイドのDBの状態とクライアントのDBの状態と、クライアントに同期する必要が無いものがある可能性がありますが、同期するテーブルとしないテーブルを選択することは可能でしょうか
A: このテーブルはテーブルごとにファイルを分けることになります。
Q: 同期を手動で実行することは可能でしょうか。バックグラウンドフェッチで任意のタイミングで同期処理を開始させ、完了したらcompletionHandlerを実行したい。
A: 変化があった時に自動で同期が開始する 今のところは手動で同期を発火させることは出来ません。 同期のスタートは自動で任せなくてはいけない。 バックグラウンドフェッチは時間が限られているので完了するかが難しい場合もあります。 手作業で開始するAPIの提供予定はあります。同期の完了を取れるかは、それは現在も可能で、Sync Progress Notificationで実行中なのか送信中なのか受信中なのか、何%なのか、完了したのかが分かるようになっています。
Q: オフラインで同期できないままアプリをバックグラウンドに送った時、次の同期されるタイミングはいつになるのでしょう
A: 次にアプリケーションを起動してObjectServerに繋いだあと、すぐにサーバーの未受信のデータ、ローカルの未アップロードのデータが同期されます。