This is not the current version. View the latest documentation
Realm React Nativeはアプリケーションのモデル層を効率的に安全で迅速な方法で記述することができます。下記の例をご覧ください:
// モデルクラスとスキーマを定義します
class Car {}
Car.schema = {
name: 'Car',
properties: {
make: 'string',
model: 'string',
miles: 'int',
}
};
class Person {}
Person.schema = {
name: 'Person',
properties: {
name: {type: 'string'},
cars: {type: 'list', objectType: 'Car'},
picture: {type: 'data', optional: true}, // オプショナル(NULL可)のプロパティ
}
};
// モデルクラスを与えてデフォルトRealmを取得します
let realm = new Realm({schema: [Car, Person]});
// Realmオブジェクトを作成してローカルDBに保存します
realm.write(() => {
let myCar = realm.create('Car', {
make: 'Honda',
model: 'Civic',
miles: 1000,
});
myCar.miles += 20; // 保存済みの値を更新することもできます
});
// 'miles > 1000'に該当するCarオブジェクトを検索します
let cars = realm.objects('Car').filtered('miles > 1000');
// 上記の条件に該当するCarオブジェクトは1件です
cars.size // => 1
// もう一つ別のCarオブジェクトを保存します
realm.write(() => {
let myCar = realm.create('Car', {
make: 'Ford',
model: 'Focus',
miles: 2000,
});
// 検索結果は自動的に最新の状態が映されます
cars.size // => 2
はじめに
Realm React Nativeはnpmを利用してインストールすることができます。ソースコードはGitHubにあります。
必要条件
- React Nativeアプリケーションの開発環境が整っている必要があります。React Nativeの開発環境のセットアップについては公式サイトの説明をご覧ください。
- React Native 0.20.0以降をサポートしています。
- 実行環境としてiOS、およびAndroidをサポートしています。
インストール
-
新しくReactNativeプロジェクトを作成します。
react-native init <project-name>
-
作成したプロジェクトのディレクトリに移動します(
cd <project-name>
)。さらにRealmを依存関係として追加します。npm install --save realm
- 作成されたXcodeプロジェクト(
ios/<project-name>.xcodeproj
)を開きます。 - サイドバーで プロジェクトファイル が選択されていることを確認し、プロジェクト設定から”iOS Deployment Target”を”8.0”以上に指定します。
- サイドバーの Libraries グループを右クリックし、 Add Files to “<project-name>” を選択します。そして
../node_modules/realm/RealmJS.xcodeproj
をファイル選択ダイアログから選択します。 - アプリケーションターゲットの設定の”General”タブを選択します。左のカラムにある Libraries > RealmJS > Products を展開し、
RealmReact.framework
を”General”タブにある Embedded Binaries セクションにドラッグします。見つからない場合はスクリーンショットをご覧ください。 - アプリケーションターゲットの設定の Build Phases タブを選択し、
RealmReact.framework
がビルドフェーズの Link Binary with Libraries セクションに追加されていることを確認してください。
-
新しくReactNativeプロジェクトを作成します。
react-native init <project-name>
-
作成したプロジェクトのディレクトリに移動します(
cd <project-name>
)。さらにRealmを依存関係として追加します。npm install --save realm
-
下記のコマンドをプロジェクトのディレクトリで実行します。
react-native link realm
-
android/app/src/main/java/com/<project-name>/MainActivity.java
を開き、さらに下記を実行します。import io.realm.react.RealmReactPackage;
を既存のimport文の下に追加します。new RealmReactPackage()
をgetPackages()
メソッドの戻り値の最後に追加します。
ここまでで、Realmを使用する準備が整いました。下記の定義をindex.ios.js
またはindex.android.js
ファイルのclass <project-name>
に記述して、Realmが正しくセットアップされているか試してみてください。
const Realm = require('realm');
class <project-name> extends Component {
render() {
var realm = new Realm({schema:[{name:'Dog', properties:{name: 'string'}}]});
realm.write(()=>{
realm.create('Dog', ['Rex']);
})
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Count of Dogs in Realm: {realm.objects('Dog').length}
</Text>
</View>
);
}
}
デバイスまたはシミュレータを使って実行できます。
APIリファレンス
Realmで使用できるすべてのクラスとメソッドに関しては、APIリファレンスをご覧ください。
サンプルコード
サンプルコードを動かすにはまずRealm React NativeのプロジェクトをGitHubリポジトリからクローンします。
git clone https://github.com/realm/realm-js.git
サンプルプロジェクトはクローンしたリポジトリのexamples/ReactExample
フォルダに格納されています。初回のみ、サンプルを動かす前に、このディレクトリでnpm install
を実行してください。
ヘルプ
- 使い方に困ったときは、StackOverflowで#realmタグを付けて質問してください。私たちは毎日StackOverflowをチェックしています。
- さらに複雑な問題に対する質問は、こちらの Slackチャットにて聞いてください。(質問は日本語で構いません)
- 問題を発見した場合はGitHubのIssuesに報告してください。できる限り、ご使用のRealmのバージョン、エラーメッセージやログ、スアックトレースやRealmのデータファイル、問題を再現可能なプロジェクトなどを添えてください。
- 機能のリクエストもGitHubのIssuesで教えてください。どのような機能が欲しいのか、また、どうしてその機能が必要なのかできるだけ具体的に教えてください。
もしクラッシュレポートツール(CrashlyticsやHockeyAppなど)を利用しているなら、ログコレクターを有効にしてください。Realmのログにはユーザーデータ以外のデバッグに有用なメタデータを含んでいます。それは私たちが問題を調査するときに非常に役に立ちます。
モデル
Realmのデータモデルは、スキーマの情報をRealmインスタンスの初期化時に渡すことによって定義されます。スキーマの情報とはモデルオブジェクトの名前、およびプロパティの名前や型などを示す一連の属性です。型は基本的な型に加えて、1対1の関連を示すobjectType
や1対多の関連を示すリスト型が指定できます。またoptional
(NULL可)やdefault
(デフォルト値)についてもここで指定します。
var 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}, // オプショナル(NULL可)のプロパティ
}
};
// CarとPersonのモデルクラスを指定してRealmを初期化します
let realm = new Realm({schema: [CarSchema, PersonSchema]});
既存のクラスを継承してモデルクラスを定義する場合は、スキーマをコンストラクタ内で定義して、コンストラクタをRealmの初期化時に渡します。
class Person {
get ageSeconds() {
return Math.floor((Date.now() - this.birthday.getTime()));
}
get age() {
return ageSeconds() / 31557600000;
}
}
Person.schema = PersonSchema;
// 注: `Person`クラスのコンストラクタをRealmに渡します
let realm = new Realm({schema: [CarSchema, Person]});
モデルクラスの定義ができていれば、オブジェクトをRealmに保存したり、検索することができます。
realm.write(() => {
let car = realm.create('Car', {
make: 'Honda',
model: 'Civic',
miles: 750,
});
// モデルに定義したすべてのプロパティを読み書きできます
console.log('Car type is ' + car.make + ' ' + car.model);
car.miles = 1500;
});
対応しているデータ型
Realmでは、次に示すデータ型を基本的なデータ型としてサポートしています: bool
、int
、float
、double
、string
、data
およびdate
。
bool
型のプロパティは、JavaScriptにおけるBoolean
型にマッピングされます。int
、float
および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の関連として、オブジェクト型のプロパティを指定するには、型にスキーマに定義した別のオブジェクトの名前(name
)を指定します。
const PersonSchema = {
name: 'Person',
properties: {
// 下記のtypeプロパティの定義はどちらも同じ意味です
car: {type: 'Car'},
van: 'Car',
}
};
オブジェクト型のプロパティを使用する場合は、Realmを初期化する際に、指定したすべてのオブジェクト型がスキーマに定義されている必要があります。
// PersonSchema内で'Car'型のプロパティとしてCarSchemaが使われているので、CarSchemaも渡す必要があります。
let realm = new Realm({schema: [CarSchema, PersonSchema]});
オブジェクト型のプロパティにアクセスする場合は、標準の文法を用いて、ネストしたプロパティとしてアクセスします。
realm.write(() => {
var nameString = person.car.name;
person.car.miles = 1100;
// JSON記法を用いて新しくCar型のプロパティを作成して代入します。
person.van = {make: 'Ford', model: 'Transit'};
// 同じCar型のプロパティであるcarとvanに同じインスタンスを設定します。
person.car = person.van;
});
リスト型のプロパティ
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; // 添字を使って各要素にアクセスします
オプショナル(NULL可)プロパティ
各プロパティはoptional
属性を用いて、オプショナル(NULL可)、または非オプショナル(NULL不可)として定義できます。
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'});
});
プライマリキー
string
型およびint
型のプロパティについてはprimaryKey
属性を用いてプライマリキーとして指定できます。プラオマリキーが定義されていると、オブジェクトの検索と更新を効率的に行えることに加え、値が重複していないことを保証できます。プライマリキーが設定されたオブジェクトは、Realmに保存した後でプライマリキーの値を変更することはできなくなります。
const PersonSchema = {
name: 'Person',
primaryKey: 'id',
properties: {
id: 'int', // プライマリキー
name: 'string'
}
};
書き込み
Realmへのオブジェクトの追加、変更、削除は、トランザクションの内部で行う必要があります。
トランザクションのオーバーヘッドは大きいのでコード中のトランザクションは、できる限り少なくなるように設計してください。
オブジェクトの生成
これまでに説明したように、オブジェクトの生成にはcreate
メソッドを使用します。
let realm = new Realm({schema: [CarSchema]);
realm.write(() => {
realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});
ネストしたオブジェクト
オブジェクト型のプロパティを持つオブジェクトは、各オブジェクトのプロパティの値をJSONを用いて再帰的に子のプロパティも含めて一度に生成できます。
let realm = new Realm({schema: [PersonSchema, CarSchema]);
realm.write(() => {
realm.create('Person', {
name: 'Joe',
// ネストしたオブジェクトを1度に再帰的に生成できます
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
オブジェクトの内容を変更することはできません。
オブジェクトを検索するもっとも基本的なメソッドは、Realm
のobjects
メソッドです。引数で与えられた型のオブジェクトをすべて取得します。
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"');
並べ替え
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 = Array.prototype.slice.call(cars, 0, 5);
Realmについて
複数のRealmを使い分ける
複数のRealmファイルを別の場所に保存して使い分けることができると便利です。例えば、事前に用意した組み込み済みデータを、メインのデータファイルとは別に読み込み専用のRealmとして利用するなどです。
Realmオブジェクトの初期化時にpath
を指定することで、Realmファイルの保存場所を指定できます。path
の指定はアプリケーションのドキュメントディレクトリからの相対パスになります。
// Realmをデフォルト値とは別の保存先を指定して取得します
let realmAtAnotherPath = new Realm({
path: 'anotherRealm.realm',
schema: [CarSchema]
});
デフォルトRealmの保存先
このドキュメントにおけるこれまでのコード例では、path
引数を指定していなかったことにお気づきでしょう。path
引数を指定しない場合は、デフォルトの保存先が使われます。デフォルトの保存先を知る、または変更するにはグローバルプロパティに定義されているRealm.defaultPath
を使用します。
スキーマバージョン
Realmの初期化時に指定できる最後の1つのプロパティはschemaVersion
です。指定しなかった場合は、デフォルトの値として0
が使われます。既存のデータからスキーマを変更した場合は、必ずschemaVersion
を初期化時に指定しなければなりません。もしスキーマが変更されているにもかかわらず、schemaVersion
を指定しなかった場合は例外が発生します。
const PersonSchema = {
name: 'Person',
properties: {
name: 'string'
}
};
// schemaVersionのデフォルトは0です
let realm = new Realm({schema: [PersonSchema]});
const UpdatedPersonSchema = {
// スキーマの`name`プロパティが同じなので`Person`オブジェクトのスキーマが
// 更新されたことになります
name: 'Person',
properties: {
name: 'string',
dog: 'Dog' // 新しいプロパティを追加
}
};
// 下記の記述はスキーマが更新されているにもかかわらず、
// スキーマバージョンを指定していないので例外が発生します。
let realm = new Realm({schema: [UpdatedPersonSchema]});
// 下記の記述ではスキーマの更新が成功し、新しいスキーマのRealmを取得します
let realm = new Realm({schema: [UpdatedPersonSchema], schemaVersion: 1});
マイグレーション
マイグレーション機能については、現在のバージョンでは上記の例に示したような、単純なスキーマの変更とschemaVersion
の更新による自動マイグレーションのみサポートしています。個々のデータのマイグレーションは未サポートです。将来的にサポートを予定しています。
変更通知イベント
トランザクションが完了した際には変更通知イベントが送られます。変更通知イベントを受けるためにはリスナーを登録します。
// Realmの変更通知イベントを監視します。
realm.addListener('change', () => {
// このタイミングでUIを更新します
...
});
// 変更通知イベントの監視を解除します
realm.removeAllListeners();
React Native ListView
List
やResults
のインスタンスをListView
のデータソースとして利用する場合は、realm/react-native
モジュールにて提供されているListView
とListView.DataSource
を利用することを強く推奨します。
import { ListView } from 'realm/react-native';
realm/react-native
モジュールが提供するListView
のAPIは完全にReact.ListView
と同じです。使い方はReact.ListViewのドキュメントをご覧ください。
トラブルシューティング
クラッシュレポート
私たちは開発者の方にクラッシュレポーターを使用していただくことを推奨しています。Realmの操作のうちの多くは(ディスクI/Oを伴う操作などと同様に)実行時に失敗する可能性があります。そのため、クラッシュレポートを収集することは、何が原因で問題が発生したのかを特定し、エラー処理の改善や不具合の修正に有効です。
多くの商用のクラッシュレポーターはログを収集するオプションを提供しています。この機能を有効にすることを強く推奨します。Realm例外が発生した時や、致命的な状態に陥った際にはメタデータの情報をログに出力しており(そこにユーザーデータは一切含まれていません)、それらのログは我々が問題を調査する際に役に立ちます。
Realmの問題や不具合を報告するには
Realmについて問題を発見した際は、GitHubで問題を報告するか、help@realm.ioにEメール、またはSlackで報告してください。その際には、こちらで問題を再現できるように、できるだけ多くの情報をあわせて教えてください。
下記に示す情報は問題を解決するために非常に役に立ちます。
- あなたが実際にやりたいこと・目的。
- 期待している結果。
- 実際に発生した結果。
- 問題の再現方法。
- 問題を再現、または理解できるサンプルコード (そのままビルドして実行できるプロジェクトだと理想的です) 。
- Realmのバージョン
- クラッシュログやスタックトレース。上述のクラッシュレポートの項目も参考にしてください。