This is not the current version. View the latest documentation

イントロダクション

Realm JavaScriptはアプリケーションのモデル層を効率的に安全で迅速な方法で記述することができます。React NativeNode.jsで動作します。

下記はRealmを利用するコードの簡単なサンプルです。

const Realm = require('realm');

// モデルとモデルのプロパティを定義します
// Define your models and their properties
const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     'string',
    birthday: 'date',
    cars:     {type: 'list', objectType: 'Car'},
    picture:  {type: 'data', optional: true}, // オプショナル(NULL可)のプロパティ
  }
};

Realm.open({schema: [CarSchema, PersonSchema]})
  .then(realm => {
    // Realmオブジェクトを作成してローカルDBに保存します
    realm.write(() => {
      const myCar = realm.create('Car', {
        make: 'Honda',
        model: 'Civic',
        miles: 1000,
      });
      myCar.miles += 20; // 保存済みの値を更新することもできます
    });

    // 'miles > 1000'に該当するCarオブジェクトを検索します
    const cars = realm.objects('Car').filtered('miles > 1000');

    // 上記の条件に該当するCarオブジェクトは1件です
    cars.length // => 1

    // もう一つ別のCarオブジェクトを保存します
    realm.write(() => {
      const myCar = realm.create('Car', {
        make: 'Ford',
        model: 'Focus',
        miles: 2000,
      });
    });

    // 検索結果は自動的に最新の状態が反映されます
    cars.length // => 2
  });

Realmをサーバーサイド(Node.js)で利用する場合は、Realm Object Serverのドキュメントも合わせてご覧ください。

はじめに

インストール

Realm JavaScriptはnpmを利用してインストールすることができます。ソースコードはGitHubにあります。

Prerequisites

  • React Nativeアプリケーションの開発環境が整っている必要があります。React Nativeの開発環境のセットアップについては公式サイトの説明をご覧ください。
  • 実行環境としてiOS、およびAndroidをサポートしています。
  • React Native 0.31.0以降をサポートしています。 ```

Installation

  • 新しくReact Nativeプロジェクトを作成します。

    react-native init <project-name>

  • 作成したプロジェクトのディレクトリに移動します(cd <project-name>)。さらにrealmを依存関係として追加します。

    npm install --save realm

  • 次に、react-nativeを使ってプロジェクトとrealmネイティブモジュールをリンクします。

    react-native link realm

Android環境で利用する場合の注意: バージョンによっては、rnpmが正しくない設定を生成することがあります。Gradleを正しく更新している(android/settings.gradleandroid/app/build.gradle)にもかかわらず、Realmモジュールの追加に失敗することがあります。その場合は、まずreact-native linkコマンドでRealmモジュールが正しく追加されるかどうかを確認し、失敗する場合は、下記の手順を用いて手作業にてライブラリをリンクしてください。

  1. android/settings.gradleファイルに下記の2行を追加します。

    include ':realm'
    project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
  2. android/app/build.gradledependenciesに下記のようにcompileの指定を追加します。

    dependencies {
        compile project(':realm')
    }
  3. MainApplication.javaimport文とパッケージをリンクする指定を追加します。

    import io.realm.react.RealmReactPackage; // add this import
    
    public class MainApplication extends Application implements ReactApplication {
        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new RealmReactPackage() // add this line
            );
        }
    }

ここまでで、Realmを使用する準備が整いました。下記の定義をindex.ios.jsまたはindex.android.jsファイルのclass <project-name>に記述して、Realmが正しくセットアップされているか試してみてください。

const Realm = require('realm');

class <project-name> extends Component {
  constructor(props) {
    super(props);
    this.state = { realm: null };
  }

  componentWillMount() {
    Realm.open({
      schema: [{name: 'Dog', properties: {name: 'string'}}]
    }).then(realm => {
      realm.write(() => {
        realm.create('Dog', {name: 'Rex'});
      });
      this.setState({ realm });
    });
  }

  render() {
    const info = this.state.realm
      ? 'Number of dogs in this Realm: ' + this.state.realm.objects('Dog').length
      : 'Loading...';

    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          {info}
        </Text>
      </View>
    );
  }
}

デバイスまたはシミュレータを使って実行できます。

ここで説明している内容はRealm Node.js SDKのDeveloper Editionのインストール方法です。Professional EditionまたはEnterprise Editionを利用する場合は、メールに記述されているインストール方法をご覧ください。

Realm Node.jsのインストールはNode Package Managerを利用します。

npm install --save realm

インストールが完了すれば、アプリケーション内でrequire('realm')と記述するだけで利用できます。

var Realm = require('realm');

サンプルコード

realm-jsのGitHubリポジトリにてサンプルコードが公開されています。

Android環境で実行する場合は、NDKのインストールとANDROID_NDK環境変数を設定する必要があります。

export ANDROID_NDK=/usr/local/Cellar/android-ndk/r10e

ヘルプ

  • 使い方に困ったときは、StackOverflowで#realmタグを付けて質問してください。私たちは毎日StackOverflowをチェックしています。
  • さらに複雑な問題に対する質問は、こちらの Slackチャットにて聞いてください。(質問は日本語で構いません)
  • 問題を発見した場合はGitHubのIssuesに報告してください。できる限り、ご使用のRealmのバージョン、エラーメッセージやログ、スアックトレースやRealmのデータファイル、問題を再現可能なプロジェクトなどを添えてください。
  • 機能のリクエストもGitHubのIssuesで教えてください。どのような機能が欲しいのか、また、どうしてその機能が必要なのかできるだけ具体的に教えてください。

もしクラッシュレポートツール(CrashlyticsやHockeyAppなど)を利用しているなら、ログコレクターを有効にしてください。Realmのログにはユーザーデータ以外のデバッグに有用なメタデータを含んでいます。それは私たちが問題を調査するときに非常に役に立ちます。

モデル

Realmのデータモデルは、スキーマの情報をRealmインスタンスの初期化時に渡すことによって定義されます。スキーマの情報とはモデルオブジェクトの名前、およびプロパティの名前や型などを示す一連の属性です。型は基本的な型に加えて、1対1の関連を示すobjectTypeや1対多の関連を示すリスト型が指定できます。またoptional(NULL可)やdefault(デフォルト値)についてもここで指定します。

const Realm = require('realm');

const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     'string',
    birthday: 'date',
    cars:     {type: 'list', objectType: 'Car'},
    picture:  {type: 'data', optional: true}, // optional property
  }
};

// Initialize a Realm with Car and Person models
Realm.open({schema: [CarSchema, PersonSchema]})
  .then(realm => {
    // ... use the realm instance to read and modify data
  })

クラス

現時点では、クラスを用いたモデル定義はReact Nativeのみ利用できます。Node環境では利用できません。

ES2015から導入されたクラス構文やクラス継承を利用してモデルを定義するには、コンストラクタでスキーマを定義します。

class Person {
  get fullName() {
    return this.firstName + ' ' + this.lastName;
  }
}

Person.schema = {
  name: 'Person',
  properties: {
    firstName: {type: 'string'},
    lastName: {type: 'string'}
  }
};

定義したクラスはRealmファイルを開く際のコンフィギュレーションオブジェクトのschemaプロパティにそのまま渡せます。

Realm.open({schema: [Person]})
  .then( /* ... */ );

下記のようにしてクラスのプロパティにアクセスできます。

realm.write(() => {
  const john = realm.create('Person', {
    firstName: 'John',
    lastName: 'Smith'
  });
  john.lastName = 'Peterson';
  console.log(john.fullName); // -> 'John Peterson'
});

対応しているデータ型

Realmでは、次に示すデータ型を基本的なデータ型としてサポートしています: boolintfloatdoublestringdataおよびdate

  • bool型のプロパティは、JavaScriptにおけるBoolean型にマッピングされます。
  • intfloatおよびdouble型のプロパティは、JavaScriptにおけるNumber型にマッピングされます。内部的にはintおよびdouble型は64ビットの値として保存されます。一方float型は32ビットの値として保存されます。
  • string型のプロパティはString型にマッピングされます。
  • data型のプロパティはArrayBuffer型にマッピングされます。
  • date型のプロパティはDate型にマッピングされます。

基本的なデータ型をプロパティに指定する場合は、省略記法が使えます。プロパティの属性を含むオブジェクトを渡す代わりに、型名を示す文字列を渡します。

const CarSchema = {
  name: 'Car',
  properties: {
    // 下記のtypeプロパティの定義はどちらも同じ意味です
    make:   {type: 'string'},
    model: 'string',
  }
}

関連

1対1の関連

1対1の関連として、オブジェクト型のプロパティを指定するには、型にスキーマに定義した別のオブジェクトの名前(name)を指定します。

const PersonSchema = {
  name: 'Person',
  properties: {
    // 下記のtypeプロパティの定義はどちらも同じ意味です
    car: {type: 'Car'},
    van: 'Car',
  }
};

オブジェクト型のプロパティを使用する場合は、Realmを初期化する際に、指定したすべてのオブジェクト型がスキーマに定義されている必要があります。

// PersonSchema内で'Car'型のプロパティとしてCarSchemaが使われているので、CarSchemaも渡す必要があります。
Realm.open({schema: [CarSchema, PersonSchema]})
  .then(/* ... */);

オブジェクト型のプロパティにアクセスする場合は、標準の文法を用いて、ネストしたプロパティとしてアクセスします。

realm.write(() => {
  const nameString = person.car.name;
  person.car.miles = 1100;

  // JSON記法を用いて新しくCar型のプロパティを作成して代入します。
  person.van = {make: 'Ford', model: 'Transit'};

  // 同じCar型のプロパティであるcarとvanに同じインスタンスを設定します。
  person.car = person.van;
});

####1対多の関連

1対多の関連として、リスト型のプロパティを指定するには、list型としてスキーマに指定し、同時に格納する要素をobjectTypeに指定します。

const PersonSchema = {
  name: 'Person',
  properties: {
    cars: {type: 'list', objectType: 'Car'},
  }
}

リスト型のプロパティにアクセスするとListオブジェクトが返ります。Listオブジェクトは通常のJavaScriptの配列オブジェクトとほとんど同じメソッドを持っています。大きく異なる点は、Listオブジェクトに対する変更はすべて自動的に永続化されるという点です。さらに、Listオブジェクトは取得したオブジェクトが保持しています。そのため、Listオブジェクトをプログラマが自分で生成することはできません。別のオブジェクトのプロパティとしてアクセスし、取得します。

let carList = person.cars;

// CarオブジェクトをListに追加します
realm.write(() => {
  carList.push({make: 'Honda', model: 'Accord', miles: 100});
  carList.push({make: 'Toyota', model: 'Prius', miles: 200});
});

let secondCar = carList[1].model;  // 添字を使って各要素にアクセスします

逆方向の関連

Realmの関連は一方通行です。Dogへの1対多の関連であるPerson.dogsのプロパティと、Personへの1対1の関連であるDog.ownerの2つのプロパティは、それぞれ互いに独立して動作します。Person.dogsプロパティにDogオブジェクトを追加しても、Dog.ownerプロパティにPersonのオブジェクトは自動的に追加されたりはしません。手作業でこの2つの関連の整合性を保とうとすることは、間違いを起こしやすく、複雑で冗長さを招くので、Realmでは下記に示すような、逆方向の関連(バックリンクとも呼ばれます)を表現するためのプロパティを使用することができます。

Linkng Object型のプロパティを使用することで、関連元のオブジェクトを特定のプロパティを使って取得することができます。例えば、Dogクラスにownersという自分自身を関連として保持しているPersonオブジェクトをすべて取得するというプロパティを持たせることができます。その方法はownersプロパティをlinkingObjects型として定義し、ownersプロパティはPersonクラスの関連であると指定するだけです。

const PersonSchema = {
  name: 'Person',
  properties: {
    dogs: {type: 'list', objectType: 'Dog'},
  }
}

const DogSchema = {
  name:'Dog',
  properties: {
    owners: {type: 'linkingObjects', objectType: 'Person', property: 'dogs'}
  }
}

linkingObjectsプロパティは、Listプロパティ(1対多の関連)または Objectプロパティ(1対1の関連)のいずれかを指定できます。

const ShipSchema = {
  name: 'Ship',
  properties: {
    captain: 'Captain'
  }
}

const CaptainSchema = {
  name: 'Captain',
  properties: {
    ships: {type: 'linkingObjects', objectType: 'Ship', property: 'captain'}
  }
}

linkingObjectsプロパティにアクセスするとResultsオブジェクトが返ります。Resultsオブジェクトなのでさらにクエリやソートを連鎖する操作が利用できます。 linkingObjectプロパティは取得元のオブジェクトに属していて、直接代入したり変更はできません。トランザクションがコミットされると自動的に更新されます。

スキーマを持たない状態でlinkingObjectsにアクセスするには: スキーマを指定せずにRealmファイルを開いた場合(例: Realm Functionsのコールバックなど)は、Objectインスタンスに対してlinkingObjects(objectType、property)をメソッドを呼び出すことによって inkingObjectsプロパティを取得できます。

let captain = realm.objectForPrimaryKey('Captain', 1);
let ships = captain.linkingObjects('Ship', 'captain');

### オプショナル(NULL可)プロパティ

各プロパティは`optional`属性を用いて、オプショナル(NULL可)、または非オプショナル(NULL不可)として定義できます。

```js
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     {type: 'string'},               // 必須(非オプショナル・NULL不可)プロパティ
    birthday: {type: 'date', optional: true}, // オプショナル(NULL可)プロパティ

    // オブジェクト型のプロパティは常にオプショナル(NULL可)です
    car:      {type: 'Car'},
  }
};

let realm = new Realm({schema: [PersonSchema, CarSchema]});

realm.write(() => {
  // オプショナル(NULL可)プロパティは生成時にはnullまたはundefinedを設定できます
  let charlie = realm.create('Person', {
    name: 'Charlie',
    birthday: new Date(1995, 11, 25),
    car: null,
  });

  // オプショナル(NULL可)プロパティは`null`、`undefined`、
  // または通常の値のいずれかを設定できます
  charlie.birthday = undefined;
  charlie.car = {make: 'Honda', model: 'Accord', miles: 10000};
});

上記に示す通り、オブジェクト型のプロパティは明示的に指定しなくても常にオプショナルとして扱われます。一方、List型のプロパティはオプショナルとして定義することはできず、nullをセットすることはできません。List型のプロパティを空にする際は、プロパティに空の配列をセットします。

デフォルト値

各プロパティはdefault属性を用いて、デフォルト値を定義できます。デフォルト値を設定したいプロパティは、オブジェクトの生成時にそのプロパティはundefinedのままにしておきます。

const CarSchema = {
  name: 'Car',
  properties: {
    make:  {type: 'string'},
    model: {type: 'string'},
    drive: {type: 'string', default: 'fwd'},
    miles: {type: 'int',    default: 0}
  }
};

realm.write(() => {
  // `miles`プロパティは何も指定していないのでデフォルト値の`0`が設定されます。
  // また`drive`プロパティはここで指定しているので、デフォルト値は使用されず指定した値で上書きされます。
  realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});

インデックス付きプロパティ

プロパティの定義中でindexed属性をtrueに設定すると、そのプロパティにはインデックスが付加されます。 intstring、およびbool型のプロパティはインデックスに対応しています。

You can add an indexed designator to a property definition to cause that property to be indexed. This is supported for int, string, and bool property types:

var BookSchema = {
  name: 'Book',
  properties: {
    name: { type: 'string', indexed: true },
    price: 'float'
  }
};

プロパティをインデックスに登録することは同値性を比較するクエリの速度を大幅に向上します。その代わりにオブジェクトを作成する速度は少し遅くなります。

プライマリキー

string型およびint型のプロパティについてはprimaryKey属性を用いてプライマリキーとして指定できます。プラオマリキーが定義されていると、オブジェクトの検索と更新を効率的に行えることに加え、値が重複していないことを保証できます。プライマリキーが設定されたオブジェクトは、Realmに保存した後でプライマリキーの値を変更することはできなくなります。

const PersonSchema = {
  name: 'Person',
  primaryKey: 'id',
  properties: {
    id:   'int',    // プライマリキー
    name: 'string'
  }
};

プライマリキーとして指定したプロパティは自動的にインデックスが付加されます。

書き込み

Realmへのオブジェクトの追加、変更、削除は、write()メソッドを用いてトランザクション内で行う必要があります。 トランザクションのオーバーヘッドは大きいのでコード中のトランザクションは、できる限り少なくなるように設計してください。

オブジェクトの生成

オブジェクトの生成にはcreateメソッドを使用します。

try {
  realm.write(() => {
    realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
  });
} catch (e) {
  console.log("Error on creation");
}

write()メソッド内で例外が発生した場合は、トランザクションは自動的にキャンセルされます。try/catchブロックはすべてのコード例に出てきませんが、try/catchブロックを使うことは良いプラクティスとして推奨しています。

ネストしたオブジェクト

オブジェクト型のプロパティを持つオブジェクトは、各オブジェクトのプロパティの値をJSONを用いて再帰的に子のプロパティも含めて一度に生成できます。

realm.write(() => {
  realm.create('Person', {
    name: 'Joe',
    // nested objects are created recursively
    car: {make: 'Honda', model: 'Accord', drive: 'awd'},
  });
});

オブジェクトの更新

プロパティへの代入

オブジェクトを更新するには、トランザクションの中でプロパティに値を設定します。

realm.write(() => {
  car.miles = 1100;
});

プライマリキーを使ってオブジェクトを作成・更新する

モデルにプライマリキーを指定しているなら、オブジェクトがすでに存在する場合は更新、存在しない場合は新しく追加というように、追加または更新を一度に行うことができます。この機能を利用するにはcreateメソッドの3つ目の引数にtrueを渡します。

realm.write(() => {
  // Bookオブジェクトを生成して保存します
  realm.create('Book', {primaryId: 1, title: 'Recipes', price: 35});

  // 上で保存したBookオブジェクトのPriceプロパティをプライマリキーを指定して更新します
  realm.create('Book', {primaryId: 1, price: 55}, true);
});

上記の例では、最初に保存されたBookオブジェクトはプライマリキーとしてprimaryIdプロパティを持ち、プライマリキーは1です。次の行で同じプライマリキー1を持つオブジェクトを渡し、3つ目の引数をtrueに指定します。そのため、新しくオブジェクトが作成されるのではなく、既存のオブジェクトのpriceプロパティが更新されます。nameプロパティは渡しているオブジェクトに含まれていないので、更新されず元の値が維持されます。

オブジェクトの削除

オブジェクトを削除するにはトランザクションの中でdeleteメソッドを使用します。

realm.write(() => {
  // Bookオブジェクトを作成し、保存します
  let book = realm.create('Book', {primaryId: 1, title: 'Recipes', price: 35});

  // Bookオブジェクトを削除します
  realm.delete(book);

  // `Results`、`List`、またはJavaScriptの`Array`を渡すと
  // 1度に複数のオブジェクトを削除できます
  let allBooks = realm.objects('Book');
  realm.delete(allBooks); // すべてのBookオブジェクトを削除します
});

クエリ

Realmのクエリは、どれか1つのオブジェクト型を指定して保存されているオブジェクトをRealmから取得します。検索条件を指定して結果をフィルタしたり、並べ替えることもできます。すべてのクエリと検索結果のプロパティアクセスは自動的に遅延されます。実際のデータはオブジェクトとプロパティにアクセスしたときにのみ取得されます。このことにより、大量のデータでも効率よく扱うことができます。

クエリを実行するとResultsオブジェクトが返ります。Resultsは検索結果を表します。Resultsオブジェクトの内容を変更することはできません。

オブジェクトを検索するもっとも基本的なメソッドは、Realmobjectsメソッドです。引数で与えられた型のオブジェクトをすべて取得します。

let dogs = realm.objects('Dog'); // Realmに保存されているすべてのDogオブジェクトを取得します

検索条件を指定する

filteredメソッドにクエリ文字列で検索条件を渡すことで、Resultsオブジェクトに含まれるオブジェクトをフィルタすることができます。

下記の例では、先のDogオブジェクトをすべて取得する例に少し手を加えて、colorプロパティが”tan”かつnameが”B”から始まるオブジェクトを取得します。

let dogs = realm.objects('Dog');
let tanDogs = dogs.filtered('color = "tan" AND name BEGINSWITH "B"');

現在はNSPredicateの文法のうちの次に示す一部分のみをサポートしています。基本的な比較演算子である==!=>>=<<=は数値型のプロパティに対して使用できます。==BEGINSWITH、ENDSWITHCONTAINSは文字列に対して使用できます。文字列の比較では、==[c]BEGINSWITH[c]などのように、演算子に[c]を付加することによって大文字小文字を区別しない比較を行うことができます。関連のプロパティに対して検索条件を使用する場合は、クエリ中でcar.color == 'blue'`のようにキーパスを用いて指定します。

並べ替え

1つまたは複数のプロパティを指定してResultsを並べ替えることができます。下記の例では、milesプロパティの昇順で並べ替えます。

let hondas = realm.objects('Car').filtered('make = "Honda"');

// ホンダ製(make = "Honda")かつ、走行距離(miles)の昇順
let sortedHondas = hondas.sorted('miles');

クエリの実行結果(Results)の順序はソートしなければ保証されません。パフォーマンス上の都合により、オブジェクトの挿入順は保持されません。

検索結果(Results)の自動更新(ライブアップデート)

Resultsは常に最新の状態に自動的に更新されます。このため、同じ検索条件なら繰り返し検索を実行して、結果を取得し直す必要はありません。Resultsは常に現在の最新の状態を反映します。

let hondas = realm.objects('Car').filtered('make = "Honda"');
// hondas.length == 0

realm.write(() => {
  realm.create('Car', {make: 'Honda', model: 'RSX'});
});
// hondas.length == 1

この仕組みはすべてのResultsインスタンスに(検索条件や、並べ替えの有無にかかわらず)適用されます。

Resultsが持つこの性質によって、Realmは効率的で高速な処理を実現しています。さらにアプリケーションのコードをシンプルかつリアクティブにすることを可能にします。例えば、クエリの検索結果を表示するビューに対しては、Resultsを保持して直接データの表示に使うことで、アクセスするたびに再検索することなく、常に最新のデータを表示することができます。

通知を用いてRealmのデータが更新され、UIをアップデートする必要があることを検知できます。その場合も、Resultsを取得し直す必要はありません。

取得データの数を制限

Realm以外のほとんどのデータベースには検索結果を「ページネーション(ページング)」する仕組みが備わっています(例えばSQLiteの’LIMIT’句によるものなどです)。この仕組みはディスクの過剰な読み込み、あるいは一度に大量のデータをメモリに読み込むことを避けるために使われます。

Realmのクエリは遅延実行されるので、このような「ページネーション」の仕組みはまったく必要ありません。なぜなら、Realmはクエリの実行結果の要素に対して、実際にアクセスしたときだけオブジェクトを読み込むからです。

UIや実装の都合により、クエリの実行結果の一部分だけが必要だったとします。そのときは、単にResultsオブジェクトを用いて、必要な要素にだけアクセスすれば良いのです。

let cars = realm.objects('Car');

// データを5件に制限したい場合は、
// 単に最初から5番目までのオブジェクトにアクセスします
let firstCars = cars.slice(0, 5);

## Realmについて

### Realmを開く

Realmを開くにはRealmクラスのopenスタティックメソッドを呼び出すだけです。パラメータには設定オブジェクトを渡します。これまでに登場したコード例では、設定オブジェクトにschemaキーを渡しています。

// Get the default Realm with support for our objects
Realm.open({schema: [Car, Person]})
  .then(realm => {
    // ...use the realm instance here
  })
  .catch(error => {
    // Handle the error here if something went wrong
  });

設定オブジェクトに関する完全は詳細については、APIリファレンスをご覧ください。下記にschema以外のよく使われるキーについて示します。

デフォルトRealm

これまでのコード例を見てすでにお気づきだと思いますが、path引数は指定しなくても構いません。その場合はデフォルトのRealmパスが自動的に使用されます。グローバルプロパティのRealm.defaultPathを設定するとデフォルトのパスを変更できます。

デフォルト以外のRealm

複数のRealmを使い分ける

複数のRealmファイルを別の場所に保存して使い分けることができると便利です。例えば、事前に用意した組み込み済みデータを、メインのデータファイルとは別に読み込み専用のRealmとして利用するなどです。

Realmオブジェクトの初期化時にpathを指定することで、Realmファイルの保存場所を指定できます。pathの指定はアプリケーションのドキュメントディレクトリからの相対パスになります。

// Realmをデフォルト値とは別の保存先を指定して取得します
let realmAtAnotherPath = new Realm({
  path: 'anotherRealm.realm',
  schema: [CarSchema]
});

スキーマバージョン

Realmの初期化時に指定できるもう1つのプロパティはschemaVersionです。指定しなかった場合は、デフォルトの値として0が使われます。既存のデータからスキーマを変更した場合は、必ずschemaVersionを初期化時に指定しなければなりません。もしスキーマが変更されているにもかかわらず、schemaVersionを指定しなかった場合は例外が発生します。

const PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string'
  }
};

// schemaVersionのデフォルトは0です
Realm.open({schema: [PersonSchema]});

Realmを開いた後は、たとえば次のように使用します。

const UpdatedPersonSchema = {
  // スキーマの`name`プロパティが同じなので`Person`オブジェクトのスキーマが
  // 更新されたことになります
  name: 'Person',
  properties: {
    name: 'string',
    dog:  'Dog'     // 新しいプロパティを追加
  }
};

// 下記の記述はスキーマが更新されているにもかかわらず、
// スキーマバージョンを指定していないので例外が発生します。
Realm.open({schema: [UpdatedPersonSchema]});

// 下記の記述ではスキーマの更新が成功し、新しいスキーマのRealmを取得します
Realm.open({schema: [UpdatedPersonSchema], schemaVersion: 1});

Realmを開く前に、スキーマバージョンがいくつであるかを知るには、下記のようにRealm.schemaVersionメソッドを使用します。

const currentVersion = Realm.schemaVersion(Realm.defaultPath);

同期的にRealmを開く Opening Realms

Realmのコンストラクタを使ってRealmインスタンスを生成することもできます。設定オブジェクトはコンストラクタのパラメータとして渡します。しかし、この方法は処理をブロックしてしまうので、通常は推奨いたしません。特に[マイグレーション(#migrations)]が必要な場合や、Realm Mobile Platformを使って同期されたRealmを利用する場合など、Realmファイルを開くときに時間がかかってしまうことがあります。同期されたRealmのダウンロードが終わるまで次の処理ができないということにならないように、openopenAsyncを利用することをおすすめします。

それでも同期的に開きたい場合は、下記のようにします。

const realm = new Realm({schema: [PersonSchema]});

// You can now access the realm instance.
realm.write(/* ... */);

読み取り権限しか与えられていない同期されたRealmを開く場合は、非同期メソッド(openopenAsync)を 必ず 使用しなければなりません。読み取り権限しか持たないRealmを同期的に開こうとするとエラーが発生します。

マイグレーション

データベースを使ってる場合、時間が経つにつれ、データモデルは変更されていくものです。例えば、以下のPersonモデルについて考えてみてください。

const PersonSchema = {
  name: 'Person',
  properties: {
    firstName: 'string',
    lastName: 'string',
    age: 'int'
  }
}

ここで、firstNamelastNameを一つにして、nameプロパティが必要になったとします。 そこで以下のような単純な変更をインターフェースに加えることにします。

const PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string',
    age: 'int'
  }
}

ここでのポイントは、もし以前に前のデータモデルでのデータが保存されている場合、新しく定義し直したデータモデルとディスクに保存されている古いデータモデルとの間に不整合が生じてしまいます。 マイグレーションを実行せずに新しいスキーマを用いてRealmを使おうとすると、例外が発生します。

マイグレーションを実行する

スキーマバージョンとマイグレーション処理(migration function)(データを移行しなくてよいなら不要です)を更新してマイグレーション処理を定義します。 マイグレーション処理では古いデータ構造から新しいデータ構造に変換するための処理を記述します。 マイグレーション処理を記述すると、より新しいスキーマバージョンを指定してRealmを開こうとした際に適用されます。

schemaVersionだけを更新して、マイグレーション処理を記述しなかった場合は、オートマイグレーションによりデータベースに自動的に新しいプロパティが追加され、古いプロパティは自動的に削除されます。 スキーマを更新する際に、古いプロパティを更新したり、新しいプロパティにデータを移行する必要がある場合は、マイグレーション処理を書く必要があります。

たとえば、上記のPersonクラスのマイグレーションについて考えてみましょう。 新しく追加したnameプロパティには、古いfirstNamelastNameプロパティを結合した値を設定するとします。w

Realm.open({
  schema: [PersonSchema],
  schemaVersion: 1,
  migration: (oldRealm, newRealm) => {
    // only apply this change if upgrading to schemaVersion 1
    if (oldRealm.schemaVersion < 1) {
      const oldObjects = oldRealm.objects('Person');
      const newObjects = newRealm.objects('Person');

      // loop through all objects and set the name property in the new schema
      for (let i = 0; i < oldObjects.length; i++) {
        newObjects[i].name = oldObjects[i].firstName + ' ' + oldObjects[i].lastName;
      }
    }
  }
}).then(realm => {
  const fullName = realm.objects('Person')[0].name;
});

一度マイグレーション処理が適用されると、その後は通常どおりにRealmとRealmオブジェクトが使用できます。

複数世代のマイグレーション

上記で示したマイグレーションのパターンでは、複数世代にわたるマイグレーションを実行した際に問題が起こることがあります。それはユーザーがプロパティのマイグレーションが必要なアプリのアップデートをスキップした場合に起こります。その場合、古いマイグレーションのコードを修正する必要に迫られるかもしれません。

この問題は、すべてのマイグレーションを古いものから順番に実行していくことで避けることができます。そうすることで、確実にすべてのマイグレーションが実行されて最新のデータ構造にアップデートされます。下記に示すパターンに従うことで、1度書いたマイグレーションのコードは2度と触る必要はありません。ただ、古いスキーマとマイグレーションのコードを将来にわたってもずっと保持し続ける必要があります。

下記の例を参考にしてください。

const schemas = [
  { schema: schema1, schemaVersion: 1, migration: migrationFunction1 },
  { schema: schema2, schemaVersion: 2, migration: migrationFunction2 },
  ...
]

// the first schema to update to is the current schema version
// since the first schema in our array is at
let nextSchemaIndex = Realm.schemaVersion(Realm.defaultPath);
while (nextSchemaIndex < schemas.length) {
  const migratedRealm = new Realm(schemas[nextSchemaIndex++]);
  migratedRealm.close();
}

// open the Realm with the latest schema
Realm.open(schemas[schemas.length-1]);

通知

RealmResultsList型のオブジェクトではaddListenerメソッドを使って通知のコールバックを登録できます。オブジェクトに変更があったときは毎回この変更通知コールバックが呼ばれます。

通知は「Realmに対する通知」と「コレクションに対する通知」の2種類があります。「Realmに対する通知」は書き込みトランザクションがコミットされたときに呼ばれる単純な通知の仕組みです。「コレクションに対する通知」はより洗練された通知の仕組みで、追加・削除・変更といった変更内容を細かく受け取れます。

さらに、Professional EditionとEnterprise Editionにおいてはイベントハンドリング通知が利用できます。詳しくは”The Realm Mobile Platform”のドキュメントをご覧ください。

Realmに対する通知

Realmインスタンスは書き込みトランザクションがコミットされたときは常に通知を送信します。通知にコールバックを登録するには下記のようにします。

function updateUI() {
  // ...
}

// Realmの変更通知を監視します。
realm.addListener('change', updateUI);

// ..不要になったら変更通知の監視を解除します
realm.removeListener('change', updateUI);

// ..または、このメソッドですべての通知を解除できます
realm.removeAllListeners();

コレクションに対する通知

コレクションに対する通知には細かい変更内容の情報が含まれています。変更内容の情報はインデックスの配列を用いて通知されます。インデックスの配列には、1つ前に通知を受けたときから、追加、削除、および変更されたオブジェクトのインデックスが格納されています。

コレクションに対する通知は、非同期で通知されます。初回の通知はクエリが実行完了された時点で呼ばれます。そのあとは、コレクションに格納されているオブジェクトに変更、削除、または追加が発生するトランザクションがコミットされるたびに通知されます。

通知に対するコールバックは2つの引数を受け取ります。1つ目は変更があったコレクションオブジェクト自身です。2つ目はchangesオブジェクトです。このオブジェクトは変更があったインデックスの情報をdeletionsinsertionsmodificationsという変数で保持しています。

最初の2つ、deletionsinsertions変数はコレクションに対してオブジェクトを追加・削除するたびに変更された部分のインデックスを記録しています。 コレクションがResults型の場合は、検索条件に関する値が変更されて、検索条件に新しくマッチするオブジェクトが増えたとき、あるいはマッチしなくなってオブジェクトが減ったときに変更として扱われます。List型の場合は、関連にオブジェクトが追加されたときと、関連からオブジェクトが削除されたときにも変更として扱われます。

modificationsはオブジェクトのプロパティが変更されたときにインデックスが含まれます。それは1対1や1対多の関連が変更された場合も当てはまります。ただし、逆方向の関連の変更の場合は含まれません。

class Dog {}
Dog.schema = {
  name: 'Dog',
  properties: {
    name:  'string',
    age: 'int',
  }
};
class Person {}
Person.schema = {
  name: 'Person',
  properties: {
    name:    {type: 'string'},
    dogs:    {type: 'list', objectType: 'Dog'},
  }
};

上記のようなモデルクラスが定義されているとき、dogsの所有者であるPersonオブジェクトの変更を監視するとします。その場合は、検索条件にマッチするPersonオブジェクトに下記の変更が加えられた場合に、通知が届きます。

  • Personオブジェクトのnameプロパティを変更したとき
  • Personオブジェクトのdogsプロパティに要素を追加または削除したとき
  • Personオブジェクトと関連づけがあるDogオブジェクトのageプロパティを変更したとき

このようなきめ細やかな通知の仕組みにより、変更があった際にただすべてを再読み込みするだけでなく、より分かりやすいアニメーションや表示を伴ったUIの更新を行うことができます。

// Observe Collection Notifications
realm.objects('Dog').filtered('age < 2').addListener((puppies, changes) => {

  // Update UI in response to inserted objects
  changes.insertions.forEach((index) => {
    let insertedDog = puppies[index];
    ...
  });

  // Update UI in response to modified objects
  changes.modifications.forEach((index) => {
    let modifiedDog = puppies[index];
    ...
  });

  // Update UI in response to deleted objects
  changes.deletions.forEach((index) => {
    // Deleted objects cannot be accessed directly
    // Support for accessing deleted objects coming soon...
    ...
  });


});

// Unregister all listeners
realm.removeAllListeners();

同期

Node.js版Realm(Linux)で同期機能を利用するにはProfessional Edition以上が必要です。機能を有効にするにはライセンスが必要です。

Node.js版をLinuxサーバーで動かす場合は、同期機能を有効にするために、下記のようにアクセストークンをセットしてください。

const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";

// Professional EditionのAPIをアンロック
Realm.Sync.setAccessToken(token);

Realm Mobile PlatformはRealm Mobile Databaseを拡張し、ネットワークを通じてデバイス間の自動的なデータ同期を実現します。同期されたRealmに対応するために、いくつかの新しい型とクラスが導入されました。下記以降でRealm Mobile Databaseに新しく同期のために追加されたクラスを説明します。

Userクラス

Realmユーザー(Realm.Sync.User)はRealm Object Serverの中心となるクラスです。Userクラスはユーザー名+パスワードによる認証に加え、SNSを使う方法などいくつかのサードパーティの認証方式をサポートしています。

ユーザーの作成、およびログインは次の2つの値が必要です。

  • 接続先のRealm ServerのURL
  • ユーザーを認証するための認証情報。認証情報は認証方式により変わります。(例: ユーザー名+パスワード、アクセストークンなど)

認証

ユーザーログインには認証が必要です。どのような認証プロバイダがサポートされているかについては、Realm Object Serverの認証についてのドキュメント をご覧ください。

ユーザー認証に使用するアカウント情報を表すオブジェクトは次に示すさまざまな方法で作成できます。

  • ユーザー名/パスワードの組み合わせ。
  • 組み込みのサードパーティ認証サービスによって認証されたアクセストークン。
  • カスタム認証プロバイダによって認証されたトークン(カスタム認証をご覧ください)。

ユーザー名とパスワードによる認証はRealm Object Server内に完全に閉じています。ユーザーに関するコントロールをすべて自分自身で行うことができます。その他の認証サービスを利用する場合は、アプリケーション内で外部のサービスに接続し、認証トークンを取得する必要があります。

下記にさまざまな認証プロバイダにおけるアカウント情報を表すオブジェクトの作成方法を示します。

ユーザー名/パスワード
Realm.Sync.User.login('http://my.realm-auth-server.com:9080', 'username', '[email protected]$w0rd', (error, user) => { /* ... */ });

上記のメソッドを用いてログインするためには、あらかじめユーザー登録が必要です。ユーザー登録はWebの管理者用のダッシュボードから、もしくはregisterメソッドを用います。

Realm.Sync.User.register('http://my.realm-auth-server.com:9080', 'username', '[email protected]$w0rd', (error, user) => { /* ... */ });
Google
const googleAccessToken = 'acc3ssT0ken...';
Realm.Sync.User.registerWithProvider('http://my.realm-auth-server.com:9080', 'google', googleAccessToken, (error, user) => { /* ... */ });
Facebook
const fbAccessToken = 'acc3ssT0ken...';
Realm.Sync.User.registerWithProvider('http://my.realm-auth-server.com:9080', 'facebook', fbAccessToken, (error, user) => { /* ... */ });
##### カスタム認証

```js
// The user token provided by your authentication server
const accessToken = 'acc3ssT0ken...';

const user = Realm.Sync.User.registerWithProvider(
  'http://my.realm-auth-server.com:9080',
  'custom/fooauth',
  accessToken,
  (error, user) => { /* ... */ }
);

注意: JavaScript SDKでは現在は追加のログイン情報を送ることはサポートしていません。単一のアクセストークン以外に送りたいデータがある場合は、accessToken引数にJSONを渡し、受け取った側でデコードして使用してください。

ログアウト

同期されたRealmからログアウトするのは下記のメソッドを呼ぶだけです。

user.logout();

ログアウトすると同期は停止します。ログアウトしたユーザーは同期されたRealmファイルを開くことはできなくなります。

ユーザーオブジェクトを使用する

Realm Mobile Platformでは1つのアプリケーション内で同時に複数のユーザーを利用することができます。例えばメールのクライアントアプリでは複数のアカウントを切り替えて使用できます。そのような動作を実現するために、複数のユーザーを好きなときに同時に有効にできます。認証済みのユーザーオブジェクトをすべて取得するにはRealm.Sync.User.allプロパティを利用します。

Realm URLはチルダ(~)を含むことができ、各ユーザーのディレクトリを表します。Realm URL中のチルダは自動的にユーザーIDに置き換えられます。この仕組みは、ユーザーごとに別のRealm URLを指定する場合に非常に便利です。

同期されたRealmファイル保存される場所はフレームワークによって管理されています。保存場所は必要に応じて変更できます。

Realm.Sync.User.login(/* ... */, (error, user) => {
  if (!error) {
    Realm.open({
      sync: {
        user: user,
        url: 'realm://object-server-url:9080/~/my-realm',
      },
      schema: [/* ... */]
    }).then(realm => {
      /* ... */
    });
  }
});

Realm.Sync.User.currentを用いてログイン中のユーザーを取得できます。ログイン中のユーザーが存在しない、またはすべてのユーザーがログアウト済みである場合はこのプロパティはundefinedを返します。複数のユーザーがログイン中の場合は例外が発生します。

const user = Realm.Sync.User.current;

複数のユーザーがログインしている場合は、Realm.Sync.User.allプロパティを用いてログイン済みの全ユーザーのコレクションオブジェクトを取得できます。ログイン中のユーザーが存在しなければ、空のコレクションを返します。

let users = Realm.Sync.User.all;

for(const key in users) {
  const user = users[key];

  // do something with the user.
})

同期されたRealmを使用する

Userオブジェクトと接続先のRealm Object Serverを示すURLを使用して同期されたRealmを開いた後は、通常のRealmと同様に使用できます。

realm.write(() => {
  realm.create('MyObject', jsonData);
});

var objects = realm.objects('MyObject');

同期セッション

同期されたRealmとRealm Object Serverとの接続はSessionオブジェクトとして表されます。 セッションオブジェクトはrealm.syncSessionメソッドを用いて取得します。

セッションの状態を知るにはstateプロパティを使います。このプロパティからセッションがアクティブ、サーバーと未接続、またはエラー状態のいずれかであることがわかります。

アクセスコントロール

Realm Mobile Platformでは、どのユーザーが同期されたRealmにアクセスできるのかを柔軟にコントロールできる仕組みがあります。 例えば、1つのRealmを複数のユーザーが共同で編集するようなアプリを作ることもできます。 また、あるユーザーのみが編集できるファイルを公開し、他のユーザーは読み取り専用で閲覧だけを可能にするといったことも可能です。

Realmのアクセス権限には3種類のレベルが存在します。

  • mayReadは読み取り権限が与えられていることを示します。
  • mayWriteは書き込み権限が与えられていることを示します。
  • mayManageはそのRealmに対するアクセス権限を変更できることを示します。

アクセス権限を明示的に与えなかった場合は、デフォルトの動作ではRealmファイルの作成者だけがそのファイルにアクセスできます。 唯一の例外はAdminユーザーです。Adminユーザーは常にすべてのRealmファイルに対してすべてのアクセス権限を持ちます。

書き込み権限のみという状態(例: mayReadをセットせずにmayWriteだけをセットした場合など)は現在サポートしていません。

アクセス権限の概念についてより詳しく知るには、Realm Object Serverドキュメントのアクセスコントロールセクションをご覧ください。

アクセス権限を取得する

各ユーザーに付与されたアクセス権限をすべて取得するにはUser.getGrantedPermissionsメソッドを使用します。

const permissions = user.getGrantedPermissions("currentUser");

// Permissions is a regular query
const writePermissions = permissions.filtered("mayWrite = true");

// Queries are live and emit notifications
writePermissions.addListener((collection, changes) => {
    // handle permission changes
});

特定のユーザーによって与えられたアクセス権限を取得するにはotherUserパラメータを渡します。

アクセス権限を変更する

Realmファイルのアクセス権限を変更するには、直接アクセス権限を変更する方法と、リクエストベースの2つの方法があります。

アクセス権限の付与

アクセス権限の変更(付与および取り消し)はUser.applyPermissionsメソッドを使用して、Realmファイルに対するアクセス権限を直接変更できます。

const condition = { userId: 'some-user-id' };
const realmUrl = "realm://my-server.com/~/myRealm";
user.applyPermissions(condition, realmUrl, 'read');

condition引数は次に示すいずれかのオブジェクトを含んでいなければなりません。 * userId - ユーザーIDを用いてアクセス権を付与するユーザーを指定する場合に使用します。(Realmが内部的に生成したIDです) * metadataKeymetadataValue - 'email'キーとEメールの値を指定します。ユーザー名/パスワードの認証プロバイダを使用している場合にEメールアドレスを用いてアクセス権を付与するユーザーを特定できます。

最後の引数によってユーザーに付与されるアクセス権のレベルを指定します。より高いレベルの権限は低いレベルの権限を含みます。たとえば、writereadを含んでいます。adminreadwrite権限を含みます。noneを指定するとユーザーからRealmに対するアクセス権限を取り消します。

Offer/Response

offerPermissionsAsyncメソッドから返されるトークンを用いて、Realmファイルを共有する仕組みです。

const realmUrl = 'realm://my-server.com/~/myRealm';
const oneDay = 1000 * 60 * 60 * 24;
const expiration = new Date(Date.now() + 7 * oneDay);
userA.offerPermissions(realmUrl, 'write', expiration)
  .then(token => { /* ... */ });

必要に応じてexpiresAt引数を使って、リクエストの期限を設定できます。期限の切れたトークンではRealmのアクセス権を変更することはできません。すでにトークンを使用して獲得した権限については、期限が過ぎたとしても失われることはありません。アクセス権を取り消す場合はapplyPermissionsAsyncを使用します。

受け取ったトークンはメッセージアプリで送ったり、QRコードとして表示したりして、別のユーザーに共有します。別のユーザーは、受け取ったトークンを使用してパーミッションのリクエストを了承します。

const token = "...";
userB.acceptPermissionOffer(token)
  .then(realmUrl => Realm.open({ schema: [/* ... */], sync: { user: userB, url: realmUrl }}))
  .then(realm => {
    // ..use the realm
  });

暗号化

Please take note of the Export Compliance section of our LICENSE, as it places restrictions against the usage of Realm if you are located in countries with an export restriction or embargo from the United States.

Realmでは64バイトの暗号化キーを用いてAES-256とSHA-2暗号化方式でデータベースファイルを暗号化する機能を提供しています。

var key = new Int8Array(64);  // pupulate with a secure key
Realm.open({schema: [CarObject], encryptionKey: key})
  .then(realm => {
    // Use the Realm as normal
    var dogs = realm.objects('Car');
  });

この機能を使用すると、ディスクに保存されるデータが透過的にAES-256で必要に応じて暗号/複合化され、SHA-2 HMACによって検証されます。

暗号化したRealmファイルのインスタンスを作成するには同じ暗号化キーが必要になります。

暗号化したRealmを使う場合、わずかに(10%未満)パフォーマンスが下がります。

トラブルシューティング

Realmコンストラクタが見つからない

Realmコンストラクタが見つからないというエラーが原因でアプリがクラッシュする場合は、次の手順で解決します。

まず、react-native link realmを実行してください。

問題が解決しない場合でAndroidアプリを開発しているなら、さらに下記を実行してください。

MainApplication.javaファイルに次のように記述します。 java import io.realm.react.RealmReactPackage;

そして、RealmReactPackageをパッケージのリストに追加します。

protected List getPackages() {
    return Arrays.asList(
        new MainReactPackage(),
        new RealmReactPackage() // add this line
    );
}

次の2行をsettings.gradleに追加します。

include ':realm'
project(':realm').projectDir = new File(settingsDir, '../node_modules/realm/android')

問題が解決しない場合でiOSアプリを開発しているなら、下記を実行してください。 1. すべてのシミュレータを終了しデバイスの接続を切る。 2. ターミナルからパッケージマネージャの実行を停止する。(またはターミナルを再起動する) 3. アプリケーションのディレクトリにあるiosフォルダをFinderから開く。 4. buildフォルダに移動する(注意: Atomエディタにbuildフォルダが表示されない場合は、右クリックメニューの「Finderで開く」を選択してください。) 5. buildフォルダの内容をすべて削除する。(ゴミ箱に移動する) 6. react-native run-iosを実行し際ビルドする。

Chromeを使ったデバッグが遅い

この問題は既知の問題です。理由はRealmがC++で書かれていて、ネイティブコードが実行されるからです。そのためRealmはデバイスまたはシミュレータ上で実行する必要があります。しかし、Realmのゼロコピーアーキテクチャのため、RealmオブジェクトをデバッグするたびにRPCを通じて値を送る必要があるためです。

この問題を解決するためのさまざまな方法を調査しています。進捗につきましては、こちらのGitHub issueをチェックしてください。

クラッシュレポート

私たちは開発者の方にクラッシュレポーターを使用していただくことを推奨しています。Realmの操作のうちの多くは(ディスクI/Oを伴う操作などと同様に)実行時に失敗する可能性があります。そのため、クラッシュレポートを収集することは、何が原因で問題が発生したのかを特定し、エラー処理の改善や不具合の修正に有効です。

多くの商用のクラッシュレポーターはログを収集するオプションを提供しています。この機能を有効にすることを強く推奨します。Realm例外が発生した時や、致命的な状態に陥った際にはメタデータの情報をログに出力しており(そこにユーザーデータは一切含まれていません)、それらのログは我々が問題を調査する際に役に立ちます。

Realmの問題や不具合を報告するには

Realmについて問題を発見した際は、GitHubで問題を報告するか、help@realm.ioにEメール、またはSlackで報告してください。その際には、こちらで問題を再現できるように、できるだけ多くの情報をあわせて教えてください。

下記に示す情報は問題を解決するために非常に役に立ちます。

  1. あなたが実際にやりたいこと・目的。
  2. 期待している結果。
  3. 実際に発生した結果。
  4. 問題の再現方法。
  5. 問題を再現、または理解できるサンプルコード (そのままビルドして実行できるプロジェクトだと理想的です)
  6. Realmのバージョン
  7. クラッシュログやスタックトレース。上述のクラッシュレポートの項目も参考にしてください。