AndroidでRealmの初期データをJSONから投入してみる

# はじめに
Realmを使ってアプリデータを管理しようとしたとき、初期データをどうするかという問題に出くわしました。
realmファイル自信を直接設置する方法も見かけましたが、

- 管理の対象となるrealmファイルのベースはdeviceから直接吸い出さないといけない
    - データモデルを追加したときとかにはこれだと解決できない(再度realmファイルを設置してしまうと、ユーザが登録したデータ等が吹っ飛んでしまうため)
- realmファイル編集にはRealm Browserが使えるが、このツールは１レコードずつしか入れられないため、そこそこデータを入れようとすると途方もない手作業を強要される

など、少し手間がかかりそうでした。

このエントリは他の方法を試した方法の備忘録となります。
※最近使っているのがKotlinのため、ソースコードもKotlinで書かれております。予めご了承くださいmm

# まずは結論から
上のことから、JSONデータをasset等に置いておいて、
Realmオブジェクトの初期化時に変換してデータを追加しました。
その際見つけたのが[`initialData(Realm.Transaction)`](https://realm.io/docs/java/latest/api/io/realm/RealmConfiguration.Builder.html#initialData)こちら。
realmファイルがなけりゃその時に呼ぶぜってことだったのでまさに丁度いいってことでこちらを利用。

この`initialData`の引数であるTransactionクラスの雛形としては

```kotlin:transactionl.kt
class Transaction : Realm.Transaction {
    override fun execute(realm: Realm?) {
    }
}
```
というように呼び出される際にRealmオブジェクトを渡してくれるのでただデータを入れれば良いわけです。
VersionUPや改修等でデータを更に追加する必要が出てきた際は、Migrationが別途走るので、追加データはそちらで入れるようにすれば解決できます。
ということで、Realmの初期化時に以下のようにし、必要なものさえ定義しておけばデータの初期化、追加は問題なく出来るなっていう所感でした。

```kotlin
Realm.init(this) 
val realmConfig = RealmConfiguration.Builder().name("data.realm")
        .initialData(MyTransaction()) // (1) データの初期化処理を定義
        .schemaVersion(0)
        .migration(MyMigration()) // (2) マイグレーション時にさらに追加したいときはこちらで対応
        .build()
Realm.setDefaultConfiguration(realmConfig)
```

# JSON -> Realm
今回のことを調べる上でサンプルプロジェクトを作ったのでざっくり書いておきたいと思います。
## 準備
JSONからrealmデータに変換するために、まずは少し準備をします。

1. Applicationクラスの拡張クラス追加

![AppResource_kt_-_RealmInit_-____git_RealmInit_.png](https://qiita-image-store.s3.amazonaws.com/0/161173/a02287ca-d4c8-6e62-23bf-77e5a9d81a1c.png)
上図のように、メインpackageの直下にApplicationクラスの拡張クラスを設置します。
最低限の実装としては以下のようにして使っています。
```
package sample

import android.app.Application

class AppResource : Application() {
    companion object {
        var instance: AppResource? = null
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}
```
2. AndroidManifest.xmlにAppResourceクラスを登録

![AndroidManifest_xml_-_RealmInit_-____git_RealmInit_.png](https://qiita-image-store.s3.amazonaws.com/0/161173/23b818e0-6b0b-5ebc-b903-f8aec8fd224d.png)
※manifestタグのpackage属性で少し文句を言われるかもしれませんがよしなに。

このようにApplicationの拡張クラスにシングルトンオブジェクトを持っておくことで(nullableにはなってしまいますが)、constructorでcontextを渡していないようなクラス内であってもcontextインスタンスを利用することができ、すこしスッキリするかなと思っています。(※個人的な感想です)

## いざ初期投入
今回はMigrationは考えず、`initialData()`のみを実装しました。

### データの定義

まず何よりも、投入したいデータを定義します。今回はid, title, abstractの３つのFieldを持つSampleDataクラスを定義します。

```kotlin:SampleData.kt
data class SampleData(val id: Int, val title: String, val abstract: String)
```

### Realmデータモデルの定義

次に、Realmで扱うためのデータモデルを定義します。
Realmは必要な処理が終わったらcloseせねばならず、かつcloseされるとRealmObjectからデータを取り出せなくなるという仕様のため、上で定義したものと対応するものを作ります。

```kotlin:SampleDataModel.kt
open class SampleDataModel(
        var id: Int = -1,
        var title: String = "",
        var abstract: String = ""
) : RealmObject() {
    fun toModel() = SampleData(id, title, abstract) // SampleDataに変換するために定義
}
```

### jsonデータ設置

入れるデータがなきゃこの記事の意味がねぇということで、assetsディレクトリの直下に投入データのjsonを設置します

```json:sample_data.json
[
  {
    "id": 1,
    "title": "test1",
    "abstract": "abstract1"
  },
  {
    "id": 2,
    "title": "test2",
    "abstract": "abstract2"
  },
  {
    "id": 3,
    "title": "test3",
    "abstract": "abstract3"
  }
]
```

### Transactionの拡張クラス定義
どうやって初期データを投入するかをサッと定義します。
今回はシンプルにInputStreamを直接Realmに渡して解釈してもらっています。
※JSONObjectにパースして、ループなどで回すことで細かい処理ももちろん定義できます。

```kotlin:MyTransaction.kt
class MyTransaction : Realm.Transaction {
    override fun execute(realm: Realm?) {
        realm?.let {
            AppResource.instance?.let { source ->
                it.createAllFromJson(SampleDataModel::class.java, source.resources.assets.open("sample_data.json"))
            }
        }
    }
}
```

#　さいごに
サンプルで初期化したデータをListViewに出力した結果とdeviceから吸い出したrealmオブジェクトの中身のスクショを貼っつけて終わりとしたいと思います。
長々と失礼しましたmm

<img width="750px"<img width="586" alt="result.png" src="https://qiita-image-store.s3.amazonaws.com/0/161173/fa9afc74-da61-581a-4421-e2c3eaace792.png">
入ってますねー。