Realm Object Serverを使って、リモートデバッグしてみる

Androidアプリを作っていると、

* 「いまどんな値がRealmに入ってるんだっけ？」
* 「ここの数字を２に書き変えたらうまくいくのかな？」

みたいなデバッグをしたくなることがよくあります。
adb pull / push でRealmデータベースを抜いたり入れたりするのはめんどくさすぎるので、できればリモートデバッグがしたいですよね。


「いまどんな値がRealmに入ってるんだけ？」という要求については、 @zaki50 さんによる神拡張 stetho-realm で概ね目的は果たせるのですが、「ここの数字を２に書き換えたらうまくいくのかな？」のようなデバッグはそう簡単にはできません。


## Realm Mobile Platformで実現できるか・・・？

Realm Browserを使うと、Realm Object Serverに保持されている値をさらっと書き換えたり消したりすることができます。
ということは、Realm Object Serverを介して「ここの数字を２に書き換えたらうまくいくのかな？」というデバッグをすることができるのでは？ と試してみたくなりました。

### サンプルアプリ

https://github.com/YusukeIwaki/RealmDebugSample

![Slice 2.png](https://qiita-image-store.s3.amazonaws.com/0/74571/e21d9330-b6b5-63d2-512f-d97af256351b.png)


メッセンジャーライクなアプリで、内部設計的には、Messageモデルをrealm-android-adaptersのRealmBaseAdapterでそのままリストビューに表示させてるだけです。

![Slice.png](https://qiita-image-store.s3.amazonaws.com/0/74571/9cc628ee-91e8-b36b-3bc7-f1bf26e1a69e.png)


### ソースをちょっと変える

#### build.gradle

```
realm {
  syncEnabled = true
}
```

を追記します。

#### Applicationクラス

```
Realm.init(this);
```

のようにしていたところは、

```
Realm.init(this);

SyncCredentials credentials = SyncCredentials.usernamePassword(
        "yusuke.iwaki@example.com", "hogehoge", false);
SyncUser.loginAsync(credentials, "http://localhost:9080/auth", new SyncUser.Callback() {
    @Override
    public void onSuccess(SyncUser user) {
        RealmConfiguration config = new SyncConfiguration.Builder(user, "realm://localhost:9080/~/debug").build();
        Realm.setDefaultConfiguration(config);
    }

    @Override
    public void onError(ObjectServerError error) {
    }
});
```

このように、Realm Object Serverに接続するようなコードに変えます。


### Realm Object Serverを起動する

```
./start-object-server.command 
```

これを実行すると、

```
Your admin access token is: ewoJImlkZW50aXR5IjogImFkbWluIi~~(略)~~9eCEinw==
```

みたいなのが出るので、こいつをメモ。


### ユーザを適当につくる

```
SyncCredentials credentials = SyncCredentials.usernamePassword(
        "yusuke.iwaki@example.com", "hogehoge", false);
```

でログインできるように、管理画面でユーザをつくります。

![image](https://qiita-image-store.s3.amazonaws.com/0/74571/98ea9ff0-a8f6-f12f-a82b-946e47653f15.png)


### ポートフォワーディング

アプリが `http://localhost:9080/auth` のソースでRealm Object Serverに接続できるようにするには

```
adb reverse tcp:9080 tcp:9080
```

で、端末の9080版ポートをMacにフォワードさせる必要があります。

アプリのソースを `http://10.0.3.2:9080/auth` のように、直接Realm Object Serverを向くように変えるでもOKです。


### で、アプリを実行すると・・・

![Slice 3.png](https://qiita-image-store.s3.amazonaws.com/0/74571/32f43c01-0075-90b8-0e07-fc3838d35d2a.png)

見た目は何も変わりません。

#### 「あれ、古いデータが残ってるぞ？」というときは

一旦アプリを終了させてもう一度起動すると、SyncConfigurationを反映したRealm DBが見えるようになるかと思います。

Applicationクラスで非同期でSyncConfigurationをsetDefaultしているため、初回起動時はそれより先にActivityのonCreateが呼ばれ、 `Realm.getDefaultInstance()` で正しくSync DBが見えないのです。
もう一度起動し直すことによって、Applicationクラスは初期化されずそのまま、Activityだけが初期化されるので、正しくSync DBが見えるようになる、というわけです。

### Realm BrowserでRealm Object Serverに接続

`/debug` で終わるデータベースが作られています。

![Slice 2.png](https://qiita-image-store.s3.amazonaws.com/0/74571/ef50853e-9566-6bf8-1270-5393c37c795c.png)

中を覗くと、それっぽいデータが見えます。

![image](https://qiita-image-store.s3.amazonaws.com/0/74571/e841eaa8-8ada-57cd-649b-2c28a059d562.png)


### さわってみる

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/74571/48921a25-4ac2-7cb7-1288-cef39afe6db1.gif)


リモートデバッグっぽい感じで、DBをごにょごにょできました。
Realm Browser側がオートリフレッシュがかかるので、非常に便利ですね。

### RealmChangeListenerとかもちゃんと発動しますね！

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/74571/cf798f50-7a61-c700-5935-5549d5ab57b4.gif)

サンプルアプリでは、「syncstate=0のMessageが来たら、それを１に変えて、もうしばらくしたら２に変える」という動作をするバックグラウンドサービスが動いていますが、RealmBrowserで syncstate=0にデータを書き換えた場合にもリスナーがしっかり発動してくれます。

　
ざっくり変更差分はこんな感じです。
https://github.com/YusukeIwaki/RealmDebugSample/commit/e94b3cd1ed476be50882d82b8b3d73ad92ab0624

## まとめ

Realm Object Serverの本来の使い方ではありませんが、ちょっと一工夫をすることでリモートデバッグにも使えそう、ということがわかりました。

今回試したソースは、デバッグ版でもリリース版でもSyncConfigurationを使うようになってますが、たとえば

* デバッグビルドではSyncConfigurationでリモートデバッグ可能
* リリースビルドでは従来通りRealmConfiguration

のようにしておくと、「やべー、リモートデバッグ有効にしたままリリースしちゃった！」てきな事故にはならずに済みますねｗ

　
ということで、みなさんRealm Object Server使ってみましょう！