はじめに

モバイルのデータベースとして、Realm を採用することはかなり一般的になってきたかと思います。 ただ、これまではデバイス内のローカルにとどまっていたので、 デバイス間でのデータの共有などはRealm だけで完結することは不可能でした。
しかし、先日発表された Realm Object Server によって、.realm ファイルを Linux や AWS などのサーバ上に置いて、それと同期する、といったことが可能になりました。
今回はmacOS をサーバとして、iOS と Android それぞれの同期方法を見てみたいと思います。

Realm の一般的な使い方については、こちらの記事をご覧ください
新モバイルデータベース Realm を試す!

本記事は、RealmSwift 2.0.2 および、 RealmJava 2.1.1 時点での情報です。 Realm Object Server は、現時点でベータ版のため、 今後、破壊的な変更が入る可能性があります。 最新の情報は、公式のドキュメントをご確認ください。

環境構築

Realm 公式ドキュメントに従って環境構築を行います。 macOS 用のバンドルをダウンロードし、start-object-server.commandというファイルを実行すると、 Realm Object Server が起動し、ブラウザで管理画面が開かれます。

Admin ユーザを登録すれば、準備完了です。

macOS をサーバにして同期するためには、モバイルアプリから macOS の localhost に接続する必要があります。 スマホ端末と mac を同じネットワークに接続し、mac 側の IP アドレスをアプリ内のコードで指定します。 mac の設定アプリからネットワークを開いて、IP アドレスを控えておいてください。

同期設定 ~ iOS ~

まず、ネットワーク環境に依存する定数を持つ構造体を作成します。

struct Constants {
    static let syncHost = "XXXX.XXXX.XXXX.XXXX" // 先ほど控えておいた mac の IP アドレス
    static let syncRealmPath = "sample" // .realm ファイルの名前
    static let syncServerURL = URL(string: "realm://\(syncHost):9080/~/\(syncRealmPath)")
    static let syncAuthURL = URL(string: "http://\(syncHost):9080")!
}

syncRealmPath で指定しているのは、ユーザごとのディレクトリの下に作られる.realmファイルの名前に使われます。 今回の場合、realm-object-server/root-dir/user_data の中にユーザごとのフォルダが作成され、 その中にsample.realm というファイルが生成されます。

早速、アプリ側からユーザを登録してみます。

let credential = Credential.usernamePassword(
    username: username, 
    password: password, 
    actions: [.createAccount] // ログイン時には空で OK
)
SyncUser.authenticate(with: credential, server: Constants.syncAuthURL) { user, error in
    guard let user = user else {
        print("Error on authenticate: \(error)")
        return
    }
    Realm.Configuration.defaultConfiguration = Realm.Configuration(
        syncConfiguration: (user, Constants.syncServerURL), objectTypes: [Task.self]
    )
}

SyncUser.authenticate(with:server:onCompletion:)で、Credentialをもとにした認証が行われます。 Credentialactions を指定しなければ、ログインとみなされます。 認証が成功して、SyncUser がブロックに渡ってきたら、defaultConfiguration を設定します。 objectTypes には、Object を継承したモデルクラスを指定します。

同期設定 ~ Android ~

まず、app 配下の build.gradle に、以下のように記述することで、 Realm Object Server を有効にします。

apply plugin: 'com.android.application'
apply plugin: 'realm-android'

android {
    // ... 省略
}

// 以下を追記することで、 Sync を有効化
realm {
    syncEnabled = true
}

// ... dependencies など

iOS の時と同じように、IP アドレスなどを管理するクラスを作ります。

public class Constants {
    public static final String SYNC_HOST = "XXXX.XXXX.XXXX.XXXX";
    public static final String SYNC_REALM_PATH = "sample";
    public static final String SYNC_AUTH_URL = "http://" + SYNC_HOST + ":9080";
    public static final String SYNC_SERVER_URL = "realm://" + SYNC_HOST + ":9080/~/" + SYNC_REALM_PATH;
}

ユーザを登録します。

// 最後の boolean は、登録かログインかを示すフラグ。ログインなら false
SyncCredentials credentials = SyncCredentials.usernamePassword(username, password, true);

SyncUser.loginAsync(SyncCredentials.usernamePassword(username, password, true), SYNC_AUTH_URL, new SyncUser.Callback(){
    @Override
    public void onSuccess(SyncUser user) {

        SyncConfiguration defaultConfig = new SyncConfiguration
                .Builder(user, Constants.SYNC_SERVER_URL)
                .build();
        Realm.setDefaultConfiguration(defaultConfig);
    }

    @Override
    public void onError(ObjectServerError objectServerError) {
        Log.e(TAG, "onError: ", objectServerError);
    }
});

以前は User というクラスを使って認証していましたが、2.1.0 からSyncUserというクラスを使うようになりました。 あまりに汎用的な名前のために変更されたようです。

まとめ

現状、作成したデータは同一のユーザからしか見えないので、 ユーザ同士でデータを共有する等はできません。 ですので、Firebase などの mBaaS のストレージを使っているアプリが、 即座に Realm Object Server に乗り換えられる訳ではありません。

しかし、公式 Slack グループ内での発言ではありますが、 遠くない将来に実装されることになるだろう、と Realm の中の人がおっしゃっていたので、 共有機能の実装が公開される日も近いかもしれません。 公開設定ができるようになれば、さらに活用の場が広がりますね。

(現状でも、全ユーザがログイン可能な単一のアカウントを作成することによって、 データを共有することは可能です。)

おまけ

Realm ファイルの中を見ることのできる Realm Browser ですが、 データベースを元に、Swift, Objective-C, Java のモデルのコードを生成する機能が付いています。 (始めにダウンロードしたバンドル内に Realm Browser.app も同梱されています)

.realm ファイルを開き、

「File」 -> 「Save Model Definitions」

を選択すると、選択肢が現れます。

Realm Object Server の発表を機に、プラットフォームをまたいだアプリでの Realm の採用がさらに加速することが見込まれます。 開発時には、この機能が役立つこと間違いなしです!