古いバージョンのドキュメントを表示しています。
最新版のドキュメントはこちらからご覧になれます

Swift から Realm を使われる場合は、Realm Swift を使用することをお考えください。

はじめに

Download Realm または、ソースコードは realm-cocoa on GitHub

必要条件

  • iOS7.0 または、 Mac OSX 10.9以上
  • Xcode 6以上
  • Objective‑C と Swift の両方でサポートされています。

インストール

  1. ここから 最新の Realm をダウンロードしてください。
  2. この中にある ios/ または osx/ フォルダから Realm.framework を Xcode のナビゲータエリアにドラッグ&ドロップしてください。この時、 Copy items if needed にチェックが入ってることを確認してから Finish をクリックしてください。
  3. ナビゲータエリアでプロジェクトをクリックし、アプリの Target を選択します。 Build Phases タブの Link Binary with Libraries から、 libc++.dylib を追加してください。
  4. Swift の場合は、 さらに Swift/RLMSupport.swift も同様にドラッグ&ドロップし、 Copy items if needed にチェックが入った状態で、ナビゲータエリアに追加してください。
  1. ここから 最新の Realm をダウンロードしてください。
  2. この中にある ios/ または osx/ フォルダから Realm.framework を Xcode のナビゲータエリアにドラッグ&ドロップしてください。この時、 Copy items if needed にチェックが入ってることを確認してから Finish をクリックしてください。
  3. ユニットテストのターゲットの “Build Settings” タブの “Framework Search Paths” で Realm.framework のパスを追加してください。
  4. Swift の場合は、 さらに Swift/RLMSupport.swift も同様にドラッグ&ドロップし、 Copy items if needed にチェックが入った状態で、ナビゲータエリアに追加してください。
  5. iOS8 のプロジェクトで Realm を使われている場合は、”Build Phases” に “Run Script Phase” を作成し、以下のスニペットをコピーしてください。 ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/Resources/strip-frameworks.sh この手順は、ユニバーサルバイナリをアーカイブする時に AppStore のサブミット時のバグを回避するために必要となります。 注意: Dynamic Framework は、OSX と iOS8 以上でのみ使用可能です。
  1. CococaPods 0.37.1 またはそれ以降のバージョンをインストールしてください([sudo] gem install cocoapods)
  2. Podfile のアプリのターゲットに pod 'Realm' とテストターゲットに pod 'Realm/Headers' を追加してください。
  3. コマンドラインで pod install を実行してください。
  4. CocoaPods によって作られた .xcworkspace ファイルを開いてください。 詳しくは CocoaPods の Webサイト をご覧ください。
  1. Carthage 0.7.2 またはそれ以降のバージョンをインストールください。
  2. github "realm/realm-cocoa" をCartfileに追加してください。
  3. carthage update を実行してください。
  4. iOS: Realm.frameworkCarthage/Build/iOS/ ディレクトリから Xcode の “General” タブにある “Linked Frameworks and Libraries” にドラッグしてください。 ** OS X:** Realm.frameworkCarthage/Build/Mac/ ディレクトリから Xcode の “General” タブにある “Embedded Binaries” にドラッグしてください。
  5. iOS アプリのターゲットの “Build Phases” の “+” ボタンをクリックし、以下のような “New Run Script Phase” を追加します。

    /usr/local/bin/carthage copy-frameworks

    この時 “Input Files” にはフレームワークのパスを指定します。

    $(SRCROOT)/Carthage/Build/iOS/Realm.framework

    このスクリプトは、ユニバーサルバイナリをアーカイブする時に AppStore のサブミット時のバグを回避するために必要となります。

詳しくは、Carthage のプロジェクトページ をご覧ください。

Realm Browser

RealmBrowser は、Realm の中で使われるている .realm ファイルを閲覧、編集する Mac アプリです。最新のRealmbrowser/ フォルダの中に入っています。 また、Tools > Generate demo database を選択することでサンプルデータを含んだ、テスト用の Realm データベースを作ることもできます。

Realm ファイルの特定の仕方が分からない方は、この StackOverflow の回答をご覧ください。

Xcode Plugin

Xcode プラグインを使うことで Realm モデルファイルの作成が簡単になります。

この Xcode プラグインをインストールする一番簡単な方法は、Alcatraz で “RealmPlugin” と検索することです。

また、手動でもインストールする場合は、release zipplugin/RealmPlugin.xcodeproj をビルドすることでインストールができます。

プラグインをインストール後に Xcode を再起動すると、Xcode で新しくファイルを作成時(File > New > File… または ⌘N)に Realm Model が追加されています。

API Reference

Realm で使用できる Class、Method に関しては API Reference をご覧ください。

サンプル

最新の Realmexamples/ ファルダに iOS/Mac のそれぞれのサンプルコードがあります。Realm の Migration(マイグレーション)、 UITableViewController との使い方、Encryption(暗号化)、コマンドラインツールなど様々な機能が紹介されていますので是非、ご参考にしてください。

ヘルプ

  • 直接質問をしたい場合は、こちらの Slack グループでお気軽にご質問ください(Slack グループは日本語です)
  • バグ報告や機能リクエストについては GitHub レポジトリにご投稿ください。
  • Discussions & Support: [email protected].
  • StackOverflow: 以前の質問はStackOverflowで #realm をご覧ください。
  • Community Newsletter に参加することで定期的にRealmに関するの Tips や UseCase、ブログポストやチュートリアルなど、Realm の最新情報が届きます。

モデル

Realm で使うモデルクラスは、一般的な のクラスで定義できます。 RLMObject クラスまたは、自分で作った Realm モデルクラスのサブクラスを作ることで簡単に作成できます。 また、他の クラスのように、メソッドやプロトコルを追加することもできます。 注意することは、Realm モデルクラスのインスタンスは、他のスレッドへの受け渡しができません。また、Realm モデルクラスのプロパティのインスタンス変数に直接、アクセスすることはできません。

XcodePlugin をお使いの場合は、 “New File…” からテンプレートを選択することで Realm モデルクラスを簡単に作成できます。

リレーションシップやネストしたデータ構造も、オブジェクトの型のプロパティを持たせることや RLMArray を使って作れます。

#import <Realm/Realm.h>

@class Person;

// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person   *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>

// Person model
@interface Person : RLMObject
@property NSString      *name;
@property NSDate        *birthdate;
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>

// Implementations
@implementation Dog
@end // none needed

@implementation Person
@end // none needed

Realm は、開発者が定義した Realm モデルクラスをアプリ起動時に、パースし読み込んでいます。たとえ一度も使わない場合であっても Realm モデルクラスは、正しく定義するようにしてください。

詳しくは RLMObject をご覧ください。

プロパティの型

Realmでは、以下のプロパティが使えます。 BOOL, bool, int, NSInteger, long, long long, float, double, CGFloat, NSString, NSDate(少数点以下は切り捨て), NSData

一対一や一対多のようなリレーションシップのために RLMArray<Object>RLMObject のプロパティも定義できます。RLMObject のサブクラスも使えます。

プロパティ属性

Realm では nonatomic, atomic, strong, copy, weak のような Objective‑C のプロパティ属性が無視されます。 Realm の中では、独自の最適化されたものが裏側で使われます。 そのため、混乱を避けるために、Realm モデルクラスを宣言するとき、プロパティ属性を付けないことを推奨しています。 プロパティ属性を付けた場合、RLMObject がRealmに保存されるまで有効になります。 setter, getter 属性でセッター/ゲッターの名前を変えた場合、RLMObject が Realm に追加される前かどうかに関わらず使うことができます。

Realmモデルクラスのカスタマイズ

クラスメソッドをオーバーライドすることで、さらに機能を追加することもできます:

  • +indexedProperties: 指定したプロパティにインデックスを作成できます。 ```objc @interface Book : RLMObject @property float price; @property NSString *title; @end

@implementation Book + (NSArray *)indexedProperties { return @[@”title”]; } @end ```

  • +defaultPropertyValues: プロパティのデフォルト値を設定することができます。Swiftでは、プロパティのデフォルト値を定義することができます。Swiftの場合は、defaultPropertyValues() を使うより、そちらをお使いください。 ```objc @interface Book : RLMObject @property float price; @property NSString *title; @end

@implementation Book + (NSDictionary *)defaultPropertyValues { return @{@”price” : @0, @”title”: @””}; } @end ```

  • +primaryKey: プライマリキーが指定できます。プライマリーを指定することで、効率的に取得、アップデートでき、それぞれのオブジェクトをユニークに保てます。 ```objc @interface Person : RLMObject @property NSInteger id; @property NSString *name; @end

@implementation Person + (NSString *)primaryKey { return @”id”; } @end ```

  • +ignoredProperties: 指定されたプロパティは、Realm に保存されず無視されます。 ```objc @interface Person : RLMObject @property NSInteger tmpID; @property (readonly) NSString *name; // read-only properties are automatically ignored @property NSString *firstName; @property NSString *lastName; @end

@implementation Person + (NSArray *)ignoredProperties { return @[@”tmpID”]; } - (NSString *)name { return [NSString stringWithFormat:@”%@ %@”, self.firstName, self.lastName]; } @end ```

書き込み

Realm へのオブジェクトの追加、変更、削除は、必ずトランザクションを使って行ってください。

Realm モデルクラスは、インスタンス化し、他の オブジェクトと同じように使うことができます。スレッド間やアプリの再起動時に Realm オブジェクトのデータを共有するときは、Realm にデータを一度保存しなければいけません。これらの操作は、トランザクションの中で行ってください。

オブジェクトの追加

以下のようにオブジェクトを追加します。

// Create object
Person *author = [[Person alloc] init];
author.name    = @"David Foster Wallace";

// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)

// Add to Realm with transaction
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];

オブジェクトを Realm に追加した、その後はずっと使用することができ、全ての変更が記録されていきます。(ただし、変更時は、トランザクションを使わなければいけません) もし、同じ Realm を複数のスレッドで共有している場合、トランザクションがコミットされた時点で、別のスレッドにもその変更が適用されます。

書き込み処理が行われている間は、他の処理をブロックしていることになります。これは、他の Persistence システムでも同じことが起こり、一般的なベストプラクティスを使うことをオススメします。 バックグランド処理 をご覧ください。

Realm は MVCC アーキテクチャーであるため、Write トランザクションが開始されている状態でも、読み込み処理は正しくできます。同時に複数のスレッドから書き込みする場合でない限り、長めの Write トランザクションを使うことをオススメします。

詳しくは、RLMRealmRLMObject をご覧ください。

オブジェクトの更新

オブジェクトの更新は、Write トランザクションの中で行ってください

// Update an object with a transaction
[realm beginWriteTransaction];
author.name = @"Thomas Pynchon";
[realm commitWriteTransaction];

Realm モデルクラスで primary key を定義してる場合にオブジェクトを更新する場合や、オブジェクトを追加する場合は、+[RLMObject createOrUpdateInRealm:withValue:] を使ってください。

// Creating a book with the same primary key as a previously saved book
Book *cheeseBook = [[Book alloc] init];
cheeseBook.title = @"Cheese recipes";
cheeseBook.price = @9000;
cheeseBook.id = @1;

// Updating book with id = 1
[realm beginWriteTransaction];
[Book createOrUpdateInRealm:realm withValue:]cheeseBook];
[realm commitWriteTransaction];

ここで id = 1 の Book オブジェクトが Realm に保存されていない場合は、新しい Book オブジェクトを追加します。

オブジェクトの削除

オブジェクトを削除するときには、Write トランザクションの中で、-[RLMRealm deleteObject:] に削除したいオブジェクトを引数に呼び出してください。

Book *cheeseBook = ... // Book stored in Realm

// Delete an object with a transaction
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];

Realm に保存されてるオブジェクトを全て削除することもできます。注意することとして、Realm ファイルでは、ディスクスペースを効率的に再利用するために、サイズを維持したままにします。

// Delete all objects from the realm
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];

クエリ

クエリを実行すると RLMObject オブジェクトを含んだ、RLMResults オブジェクトが結果として返ってきます。 RLMResults は、NSArray と似たようなインターフェイスを持ち、添え字アクセスでオブジェクトにアクセスすることもできます。 NSArray と違う点は、RLMResults は、RLMObject のみを含むことができるという点です。

Realm 上での、プロパティアクセスを含む、全てのクエリは遅延評価されてます。プロパティにアクセスした時に、初めてデータが読み込まれます。

クエリを実行したときに返ってくる結果は、データのコピーではありません。トランザクションを使ってそのデータを変更した場合、ディスクのデータを変更したことになります。 また、RLMResult に含まれる RLMObject から、関連 のあるオブジェクトをフェッチすることもできます。

オブジェクトの取得

最も、基本的なオブジェクトのフェッチ方法は +[RLMObject allObjects] です。 これは、指定したクラスのインスタンスを defaultRealm 内から全てフェッチしてきます。

// Query the default Realm
RLMResults *dogs = [Dog allObjects]; // retrieves all Dogs from the default Realm

// Query a specific Realm
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // get a specific Realm
RLMResults *otherDogs = [Dog allObjectsInRealm:petsRealm]; // retrieve all Dogs from that Realm

Predicate を使ったフェッチ

普段から NSPredicate の扱いに慣れているなら、Realm でのオブジェクトの取得仕方を知っているも同然です。RLMObject, RLMRealm, RLMArray, RLMResult は全て NSPredicate インスタンスまたは、Predicate の構文を使ってのオブジェクトのフェッチをサポートしています。

例えば、以下のように [RLMObject objectsWhere:] は、Dog クラスの color = tan で、name の値が B からはじまる Dog クラスのインスタンスを defaultRealm 内から全てフェッチしてきます。

// Query using a predicate string
RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];

// Query using an NSPredicate
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
                                                     @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];

詳しくは、Apple’s Predicates Programming Guide をご覧ください。Realm では、たくさん一般的な Predicate 構文をサポートしています。

  • 比較が、プロパティ名と定数で使えます。少なくとも一つのオペランドは、プロパティ名でないといけません。
  • 比較演算子 ==, <=, <, >=, >, !=, BETWEEN が、int, long, long long, float, double, NSDate で使えます。 Ex.) age == 45
  • オブジェクトの同一性 ==, != Ex.) [Employee objectsWhere:@”company == %@”, company]
  • bool型では、==, != が使えます。
  • NSString型、NSData型では、==, !=, BEGINSWITH, CONTAINS, ENDSWITH が使えます。 Ex.) name CONTAINS 'Ja'
  • 文字列の比較 Ex.) name CONTAINS[c] 'Ja'
  • 論理演算: “AND”, “OR”, “NOT” が使えます。 Ex.) name BEGINSWITH 'J' AND age >= 32
  • いずれかの条件と一致するかどうかの IN Ex.) name IN {'Lisa', 'Spike', 'Hachi'}
  • nil との比較 Ex.) [Company objectsWhere:@"ceo == nil"]
  • いずれかの要素が条件と一致するかどうかの ANY Ex.) ANY student.age < 21
  • 範囲が指定できる BETWEEN Ex. [Person objectsWhere:@"age BETWEEN %@", @[42, 43]]

詳しくは、[RLMObject objectsWhere:] をご覧ください。

ソート

RLMResults は、プロパティの値でソートすることができます。 たとえば、以下の例では、[RLMResults sortedResultsUsingProperty:ascending:] によって、特定の Dog オブジェクトを name の値をアルファベット順でソートしてから取り出しています。

// Sort tan dogs with names starting with "B" by name
RLMResults *sortedDogs = [[Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"]
                               sortedResultsUsingProperty:@"name" ascending:YES];

詳しくは、[RLMObject objectsWhere:][RLMResults sortedResultsUsingProperty:ascending:] をご覧ください。

連続したクエリの実行

他のデータベースと比較した時に、Realmを使う利点として、小さなオーバーヘッドで、連続してクエリが実行できる点が挙げられます。 たとえば、color = tan で、 nameB からはじまる Dogオブジェクトをフェッチしたい場合、以下のように連鎖的にメソッドを呼び出すことができます。

RLMResults *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];

Realms

デフォルトRealm

すでにお気付きだと思いますが、これまで [RLMRealm defaultRealm] を呼ぶことで、変数 realm を初期化してきました。 このメソッドは、アプリの Documents フォルダにある、“default.realm” ファイルの RLMRealm オブジェクトを返します。

+[RLMRealm setDefaultRealmPath:] を使うことで、デフォルト Realm へのパスを変更できます。この機能は、特にテスト時や iOS8 で利用可能の Shared Container で役立ちます。

Realm の API にあるたくさんのメソッドで、Realm インスンタスを引数としてとるものと、自動でデフォルト Realm が使われる両方があります。例えば、[RLMObject allObjects] は、[RLMObject allObjectsInRealm:[RLMRealm defaultRealm]] と同じ意味になります。

その他のRealm

場合によっては、複数の Realm が必要な時があります。バンドルデータとして Realm を用意しておき、それを read-only な Realm として開きたい時なのです。詳しくは、[RLMRealm realmWithPath:][RLMRealm realmWithPath:readOnly:error:] をご覧ください。

[RLMRealm realmWithPath:] で引数として渡すパスは、書き込み可能である必要があります。 Realmファイルの基本的な保存場所は、iOSでは、”Documents” フォルダ、OSXでは、”Application Support” フォルダです。 Apple の iOS Data Storage Guidelines に沿って設計してください。

In-Memory Realms

基本的には、Realm は、ディスクにデータを保存しますが、[RLMRealm inMemoryRealmWithIdentifier:] を呼ぶことで、データをメモリーに保存する In-Memory な Realm オブジェクトを作ることができます。

RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:@"MyInMemoryRealm"];

In-memory な Realm では、データをディスクに保存しません。それによりアプリの再起動時には以前に保存したデータを使用することができませんが、Query, Relationship, thread-safety は使用することができます。 これは、ディスクへの読み書きに伴うオーバーヘッドがない分、柔軟なデータアクセス機能が必要なとき有効な機能です。

注意: スコープを外れ、In-Memory な Realm インスタンスへの参照がなくなると、その Realm 内に保存されている、全てのデータは解放されます。アプリの起動中は、In-Memory な Realm インスタンスへの強参照を常に保持しておく必要があります。

スレッド間での実行

複数のスレッドで、同じ Realm を使う場合は、各スレッドで [RLMRealm defaultRealm], [RLMRealm realmWithPath:] または [RLMRealm realmWithPath:readOnly:error:] でインスタンスをそれぞれ生成する必要があります。 Realm ファイルへのパスさえ同じであれば、RLMRealm オブジェクトはディスク上で同じものを指します。

複数のスレッド間での、RLMRealm インスタンスの共有はサポートされていません。 同じ Realm ファイルを指す RLMRealm のインスタンスの realdOnly 属性は、統一しないといけません。(全て readwrite にするか全て readonly にするかしないといけません)

Realm は、膨大なデータを追加するとき、すごく効率よく動作します。 メインスレッドのブロッキングを避けるため Grand Central Dispatch を使い、その中にトランザクションを書きます。 RLMRealm オブジェクトは、スレッドセーフではないため、複数のスレッド間では共有することことができません。それぞれ、これから読み書きしたい thread/dispatch_queue 内で RLMRealm オブジェクトを生成する必要があります。 以下は、バックグランド処理で100万個のオブジェクトを追加する例です。

dispatch_async(queue, ^{
  // Get realm and table instances for this thread
  RLMRealm *realm = [RLMRealm defaultRealm];

  // Break up the writing blocks into smaller portions
  // by starting a new transaction
  for (NSInteger idx1 = 0; idx1 < 1000; idx1++) {
    [realm beginWriteTransaction];

    // Add row via dictionary. Property order is ignored.
    for (NSInteger idx2 = 0; idx2 < 1000; idx2++) {
      [Person createInRealm:realm
                  withValue:@{@"name"      : [self randomString],
                              @"birthdate" : [self randomDate]}];
    }

    // Commit the write transaction
    // to make this data available to other threads
    [realm commitWriteTransaction];
  }
});

Realm間でのオブジェクトのコピー

Realm のオブジェクトを他の Realm へコピーするには、+[RLMObject createInRealm:withValue:] へ元のオブジェクトを引数として渡すことでできます。 例えば、[MyRLMObjectSubclass createInRealm:otherRealm withValue:originalObjectInstance] のように使います。

初期データとしてのRealm

アプリをインストールした時からすぐに使えるような初期データをアプリに入れておくことは、とても一般的な方法です。以下が、Realmでのやり方です。

  1. はじめに、初期データの入った Realm を用意します。最終的にリリースする時と同じデータモデルを定義し、データを入れます。Realm ファイルは、クロスプラットフォームですので、OSX アプリや、シュミレータで動く iOS アプリでも問題ありません。(JSONImportをご覧ください)
  2. 初期データを入れるコードの終わりに、ファイルの圧縮コピーをするメソッドを使用し、Realm ファイルを圧縮してください。(-[RLMRealm writeCopyToPath:error:] をご覧ください)このメソッドを使って Realm ファイルをコピーすると、Realm のファイルサイズを軽減でき、ユーザーがアプリをダウンロードするときに軽くなります。
  3. 新しく圧縮した Realm ファイルを、アプリの Xcode のプロジェクトナビゲーターにDrag&Dropします。
  4. Xcode の Build Phase タブで、”Copy Bundle Resources” に圧縮したRealmファイルを追加します。
  5. アプリから、追加した Realm ファイルがアクセスできるようになります。 [[NSBundle mainBundle] pathForResource:ofType:] を使って、パスを見つけることができます。
  6. これで、[RLMRealm realmWithPath:readOnly:error:] で readOnly な Realm を作ることもできるし、その初期データを含んだ Realm に書き込みたい場合は、[[NSFileManager defaultManager] copyItemAtPath:toPath:error:]を使い、アプリの Documents ディレクトリにRealmファイルをコピーしてから、[RLMRealm realmWithPath:] にパスを渡し、Realmインスタンスを作るようにしてください。

Realm ファイルを使っての初期データの組み込みは、migration sample appをご参照ください。

リレーションシップ

RLMObject は、プロパティに RLMObject, RLMArray を宣言することで、他のオブジェクトと関連付けを定義することができます。 RLMArray は、NSArray と似たようなインターフェイスを持ち、添え字アクセスでオブジェクトにアクセスすることもできます。NSArrayと違う点は、RLMArray は、RLMObject のみを含むことができるという点です。 詳しくは、RLMArray をご覧ください。

以前に Person モデルを作成したと思います。ここでは、Dog モデルを作成し関連付けを行ってみましょう。

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end

To-One

モデル間で多対一や一対一の関連性を持たせたい場合は、RLMObject モデルクラスのプロパティを宣言します:

// Dog.h
@interface Dog : RLMObject
... // other property declarations
@property Person *owner;
@end

以下のようにこのプロパティを使えます:

Person *jim = [[Person alloc] init];
Dog    *rex = [[Dog alloc] init];
rex.owner = jim;

RLMObject をプロパティで使うと、ネストされたプロパティにシンプルなシンタックスでアクセスができます。 たとえば、上記の例で rex.owner.address.country とようにすると、自動的に必要なプロパティの値をフェッチされます。

To-Many

プロパティに RLMArray を宣言することで、対多の関連性を定義できます。RLMArray は、RLMObject を含み、NSMutableArray と似たようなインターフェイスを持ちます。

PersonクラスにDogクラスの対多関連を持たせるとします。まず、RLMArray<Dog> を定義します。 これは、以下のように関連するモデルの@interfeceの下にマクロを書きます。

//Dog.h
@interface Dog : RLMObject
... // property declarations
@end

RLM_ARRAY_TYPE(Dog) // Defines an RLMArray<Dog> type

RLMArray<Dog> 型を宣言できるようになりました。

// Person.h
@interface Person : RLMObject
... // other property declarations
@property RLMArray<Dog> *dogs;
@end

これで、RLMArray プロパティに対してアクセス、オブジェクトを追加したりできるようになりました。

// Jim is owner of Rex and all dogs named "Fido"
RLMArray *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjects:someDogs];
[jim.dogs addObject:rex];

Inverse Relationships

逆方向のリレーションシップを張っておくことで、特定のプロパティを用いて、関連するオブジェクトへアクセスすることができます。 -linkingObjectsOfClass:forProperty: を使い、Dogクラス定義しておくことで、紐づくオブジェクトを取得できるようになります。 以下のように read-only の owners プロパティを Dog クラスに定義することで、逆のリレーションシップを張ることができます。

@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@property (readonly) NSArray *owners; // Realm doesn't persist this property because it is readonly
@end

@implementation Dog
// Define "owners" as the inverse relationship to Person.dogs
- (NSArray *)owners {
    return [self linkingObjectsOfClass:@"Person" forProperty:@"dogs"];
}
@end

通知

Realm 内のデータの更新がかかる毎に、他の Realm インスタンスに通知を送り、それを受け取ることができます。 通知を受け取る方法は、以下のように事前にブロックとして登録しておく必要があります。

// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
    [myViewController updateUI];
}];

Notificaiton Token が保持されてる限り、登録された Notification は存在し続けます。 更新などをするためにクラス上で Notification Token の強参照を保持しておく必要があります。 Notification Token が、解放された時点で登録された Notification は自動的に抹消されます。

詳しくは、[RLMRealm addNotificationBlock:][RLMRealm removeNotificationBlock:] をご覧ください。

マイグレーション

データベースを使ってる場合、時間が経つにつれ、データモデルというのは変更されていくものです。 Realm でのデータモデルは、シンプルな インターフェイスで定義されてますので、インターフェイスに変更を加えるだけで、簡単にデータモデルを変えられます。 たとえば、以下の Person モデルについて考えてみてください。

@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

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

@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end

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

マイグレーションの実行

マイグレーション処理は、+[RLMRealm setSchemaVersion:forRealmAtPath:withMigrationBlock:] を呼ぶ時に引数として渡すブロックの中に定義します。 このとき、スキーマに関連しているバージョン毎にマイグレーション処理の分岐を書く必要があります。 マイグレーションブロックの中には、全ての古いデータモデルから新しいデータモデルへ移行させるためのロジックが書かれていないといけません。 +[RLMRealm setSchemaVersion:forRealmAtPath:withMigrationBlock:] が呼ばれた後に、自動的にマイグレーションブロックが実行されます。

たとえば、上記の Person クラスのマイグレーションについて考えてみましょう。 最低限必要なマイグレーション処理は、以下のようなものです:

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

// Notice setSchemaVersion is set to 1, this is always set manually. It must be
// higher than the previous version (oldSchemaVersion) or an RLMException is thrown
[RLMRealm setSchemaVersion:1
            forRealmAtPath:[RLMRealm defaultRealmPath]
        withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
  // We haven’t migrated anything yet, so oldSchemaVersion == 0
  if (oldSchemaVersion < 1) {
    // Nothing to do!
    // Realm will automatically detect new properties and removed properties
    // And will update the schema on disk automatically
  }
}];

// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];

Realm によって自動的にスキーマが更新されることを示すためにここでは、空のブロックでマイグレーションを実行しています。

これは最低限のマイグレーションですが、おそらく何かデータを新しいプロパティ(ここでは fullName)に入れるために、ここに処理を記述すると思います。 マイグレーションブロックの中では、特定の型の列挙処理を行うために [RLMMigration enumerateObjects:block:] を呼ぶことができます。 下記では、必要なマイグレーションロジックを適用しています。 変数 oldObject を使って既にあるデータにアクセスし、新しく更新するデータには変数 newObject を使ってアクセスしています。

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

[RLMRealm setSchemaVersion:1
            forRealmAtPath:[RLMRealm defaultRealmPath]
        withMigrationBlock:^(RLMMigration *migration, NSUInteger oldSchemaVersion) {
  // We haven’t migrated anything yet, so oldSchemaVersion == 0
  if (oldSchemaVersion < 1) {
    // The enumerateObjects:block: method iterates
    // over every 'Person' object stored in the Realm file
    [migration enumerateObjects:Person.className
                          block:^(RLMObject *oldObject, RLMObject *newObject) {

      // combine name fields into a single field
      newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                         oldObject[@"firstName"],
                                         oldObject[@"lastName"]];
    }];
  }
}];

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

バージョンの追加方法

Person クラスが、以前に異なる2つのデータモデルをとっていた場合を考えてみましょう:

// v0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

// v1
@interface Person : RLMObject
@property NSString *fullName; // new property
@property int age;
@end

// v2
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email;   // new property
@property int age;
@end

ここでのマイグレーションブロックの中に書くロジックは、以下のようになります。

[RLMRealm setSchemaVersion:2 forRealmAtPath:[RLMRealm defaultRealmPath]
                         withMigrationBlock:^(RLMMigration *migration,
                                              NSUInteger oldSchemaVersion) {
  // The enumerateObjects:block: method iterates
  // over every 'Person' object stored in the Realm file
  [migration enumerateObjects:Person.className
                        block:^(RLMObject *oldObject, RLMObject *newObject) {
    // Add the 'fullName' property only to Realms with a schema version of 0
    if (oldSchemaVersion < 1) {
      newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                oldObject[@"firstName"],
                                oldObject[@"lastName"]];
    }

    // Add the 'email' property to Realms with a schema version of 0 or 1
    if (oldSchemaVersion < 2) {
      newObject[@"email"] = @"";
    }
  }];
}];

// now that we have called `setSchemaVersion:withMigrationBlock:`, opening an outdated
// Realm will automatically perform the migration and opening the Realm will succeed
[RLMRealm defaultRealm];

詳しくは、migration sample app をご覧ください。

Linear Migrations

二人のユーザーがいると考えてください。 JP と Tim です。 JP は、よくアプリのアップデートをします。しかし、Tim は、いくつかのバージョンをスキップすることがあります。 JP にとっては、全てのバージョンをインストールしてることになるので、段階を踏んで全てのスキーマのアップデートを適用していることになります。 V0 から V1, V1 から V2 のように彼のアプリはアップデートしていきます。 それとは対照的に、Tim の場合は、V0 から V2 といきなりバージョンをすっ飛ばす恐れがあります。 マイグレーションブロックの中では、たとえどのバージョンのスキーマから始めたとしても、ネストしていない if (oldSchemaVersion < X) で必ず必要なアップデート処理は行われなければなりません。

また、他のケースで、ユーザーがバージョンのスキップをしてアプリのアップデートをしたとします。もしあなたが、”email” プロパティをバージョン2で一度、削除し V3 で再び定義したとします。 そして、ユーザーは V1 から V3 へと、V2 をスキップしてアップデートした場合、Realm は、コードのスキーマとディスクのスキーマに実質、違いはないので、”email” プロパティが一度、消されたことを自動的に判別することはできません。これは、Tim の Person クラスで起こりうることで、V1 でのプロパティの値を V3 のプロパティの値として保持される可能性があります。 V1 と V3 の間で、ストレージのスキーマが変わっていないため、このことは問題にならないかもしれません。 しかし、これを避けるため、if (oldSchemaVersion < 3) の中で、データセットが V3 のものであることを保証するために、一度 nil を代入することをオススメします。

暗号化

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.

iOS では、標準の NSFileProtection API を使用することで、少しのオーバーヘッドで Realm ファイルを保護することができます。ただし、この方法でファイルを保護する場合、2つの注意点があります。 1) NSFileProtection で Realm ファイルを保護すると、他のプラットフォームに移動させることができなくなります。(NSFileProtection は、iOS でのみ有効な機能です) 2) パスワードロックがかかるだけで、Realm ファイルは暗号化されません。 これらの制限を避けるために(もしくは、OS Xアプリでお使いの場合)、Realm が実装している暗号化方法を使用することをオススメいたします。

Realm では、64byte 暗号化キーを用いて、AES-256 と SHA-2 暗号化方式でデータベースファイルを暗号化する機能があります。

// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Open the encrypted Realm file
NSError *error;
RLMRealm *realm = [RLMRealm realmWithPath:RLMRealm.defaultRealmPath
                                      key:key
                                 readOnly:NO
                                    error:&error];
if (!realm) {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    NSLog(@"Error opening realm: %@", error);
    return;
}

// Use the Realm as normal
RLMResults *dogs = [[Dog objectsInRealm:realm where:@"name contains 'Fido'"]];

この機能を使用すると、ディスクに保存されるデータがAES-256で暗号化され、必要に応じ、複合化されます。 また、その後、RLMRealm インスタンス作成時には、同じ暗号化キーが必要になります。インスンタス作成後は、メモリ上に暗号化キーを保持され、指定したパス先にある Realm ファイルへアクセスは、自動的に暗号化キーが使用されるようになります。 たとえば、convenience メソッドを使って、defaultRealm を指定するコードは以下のようになります。

// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Set the encryption key for the default Realm
[RLMRealm setEncryptionKey:key forRealmsAtPath:RLMRealm.defaultRealmPath];

// Use the Realm as normal
RLMResults *dogs = [[Dog objectsWhere:@"name contains 'Fido'"]];

詳しくは、暗号化のサンプルコードをご覧ください。暗号化キーの作り方、キーチェーンへの安全な保存方法、Realm へのキーの渡し方などが分かるかと思います。

暗号化した Realm を使う場合、通常よりも10%未満パフォーマンスが下がります。

サードパーティのクラッシュレポート(Crashlytics や PLCrashReporter など)は、暗号化した Realm を初めて開く前に登録する必要があります。そうしないと、アプリが実際にクラッシュしていないときに誤ったエラーレポートを受け取る可能性があります。

デバッグ

Realm の LLDB 拡張とRealm Browserを使うと、Realm のデバッグをより簡単にできます。

最新の Realm の plugin/ に同梱されている、LLDB 拡張 rlm_lldb.py は、デバッグ時に RLMObject, RLMResults, RLMArray オブジェクトを調べるのに非常に役に立ちます。LLDB の拡張を入れなければ、フェッチ結果のプロパティが nil や 0 と表示されます。

Screenshot of Xcode Debugger Console

注意: LLDB スクリプトは、Objective‑C でのみ対応済みです。Swift のサポートは開発中です。

テスト

Realmを使ったアプリのテスト

defaultRealm のパスの変更

Realmを使うまたは、テストをする最も簡単な方法は、defaultRealm を使用することです。テストコードを実行する前に、+[RLMRealm setDefaultRealmPath:] でテスト用の defaultRealm のパスを変更し、アプリで使用するデータを上書きすることを避けれます。

RLMRealm の受け渡し

また、テストする部分としては、引数として RLMRealm のインスタンスが渡されるメソッドなどです。実際にアプリ実行時とテスト実行時で、異なる Realm インスタンスを投げても問題ないかテストします。 例えば、以下のテストコードは、JSON APIから GET を使ってユーザー情報を取得し、正常にデータが作成されているかをテストしています。

// Application Code
+ (void)updateUserFromServer
{
  NSURL *url = [NSURL URLWithString:@"http://myapi.example.com/user"];
  [[[NSURLSession sharedSession] dataTaskWithURL:url
                               completionHandler:^(NSData *data,
                                                   NSURLResponse *response,
                                                   NSError *error) {
    [self createOrUpdateUserInRealm:[RLMRealm defaultRealm] withData:data];
  }] resume];
}

+ (void)createOrUpdateUserInRealm:(RLMRealm *)realm withData:(NSData *)data
{
  id object = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
  [realm transactionWithBlock:^{
    [User createOrUpdateInRealm:realm withValue:object];
  }];
}

// Test Code
- (void)testThatUserIsUpdatedFromServer
{
  RLMRealm *testRealm = [RLMRealm realmWithPath:kTestRealmPath];
  NSData *jsonData = [@"{\"email\": \"[email protected]\"}"
                      dataUsingEncoding:NSUTF8StringEncoding];
  [ClassBeingTested createOrUpdateUserInRealm:testRealm withData:jsonData];
  User *expectedUser = [User new];
  expectedUser.email = @"[email protected]";
  XCTAssertEqualObjects([User allObjectsInRealm:testRealm][0],
                        expectedUser,
                        @"User was not properly updated from server.");
}

Realm.framework とテストターゲットのリンクを避ける

ダイナミックフレームワークとして Realm を使用している場合、ユニットテストターゲットが Realm を見つけれるようにしておく必要があります。 そのためには、ユニットテストの “Framework Search Paths” に Realm.framework を追加します。

テスト実行時に “Object type ‘…’ not persisted in Realm” というエラーが出る場合、Realm.framework がテストターゲットにリンクされていることが原因です。 CocoaPods をお使いの場合は、以下のように Podfile をお書きください。

target 'MyApp'
  pod 'Realm'
end

target 'MyAppTests', exclusive: true do
  pod 'Realm/Headers'
end

モデルクラスファイルをアプリのターゲットかフレームワークのターゲットにどちらか一方のみにリンクさせておきます。 そうでないと、テスト時に重複が生じ問題が発生する可能性があります。 詳しくは こちら をご覧下さい。

テストするコードは、全てテストターゲットからアクセス可能である必要があります。(Swiftでは public 修飾子をお使いください)

テストの状態の再設定

一般に、それぞれのユニットテストが独立していることかと思います。Realm ファイルをテスト毎にディスクから消し、また新しく作成することをお勧めいたします。以下のコードでは、XCTest での setUp メソッド と tearDown メソッド を使って再設定を行っています:

// Helpers

static NSString *RealmPathForTesting = @"";

static void RLMDeleteRealmFilesAtPath(NSString *path) {
  NSFileManager *fileManager = [NSFileManager defaultManager];
  [fileManager removeItemAtPath:path error:nil];
  NSString *lockPath = [path stringByAppendingString:@".lock"];
  [fileManager removeItemAtPath:lockPath error:nil];
}

// In XCTestCase subclass:

- (void)setUp {
  [super setUp];
  RLMDeleteRealmFilesAtPath(RealmPathForTesting);
  // Optionally override the default realm path.
  [RLMRealm setDefaultRealmPath:RealmPathForTesting];
}

- (void)tearDown {
  [super tearDown];
  RLMDeleteRealmFilesAtPath(RealmPathForTesting);
}

Realmのテスト

Realm 自体は、全てのコミットにおいて広い範囲のテストが行われています。 Realm は オープンソース で開発されており、私たちはあなたのコントリビュートを喜んで歓迎いたします。

REST API

Realmでは、簡単に REST API と統合ができ、いくつかの利点があります。

  • REST API のみだと常にネットとの接続が必要ですが、データをキャッシュすることでオフラインでも、アプリを動作させることができます。
  • 完全にデータをキャッシュさせておくことで、REST API だけでは、不可能なローカルでクエリを実行したり、データを閲覧したりできます。
  • データを保存させておくことで、サーバーサイド側での処理を新しい変更だけをロードするものだけに減らすことができます。

ベストプラクティス

  1. 非同期リクエスト — リクエストを投げる時や、他のブロッキング操作は、UI スレッドを止めてしまう恐れがあるので、バックグラウンド処理で行うことが推奨されています。これと同じ理由で、Realm での膨大なデータの変更は、バックグラウンド処理で行うべきです。バックグラウンドで行われた変更に対応するには、Notifications 機能を使いましょう。
  2. 膨大なデータのキャッシュ - 可能な時に事前にデータを Realm にフェッチし保存させておくことをオススメします。そのようにしておくと、いつでもローカルで保存してあるデータに対してクエリが投げられます。
  3. 挿入と更新 - Primary キーのような単一な識別子をデータセットが持っている場合、+[RLMObject createOrUpdateInRealm:withValue:] を使うことで、REST API からレスポンスを受け取ったときに、より簡単にデータを挿入/更新することができます。このメソッドは、更新が必要かどうかや、既に存在するデータかどうかなどを自動的にチェックしてくれます。

Example

以下の例は、シンプルな Realm と REST API との連携例です。この例は、Foursquare API から JSON データをフェッチしてき、Default Realm に Realm Object として保存している例です。 これに似たようなユースケース例として参考に、この ビデオ をご覧ください。 まず、Default Realm のインスタンスを生成します。そして、API からデータをフェッチしてきます。 より単純にするためにここでは、[NSData initWithContentsOfURL] を使っています。

// Call the API
NSData *response = [[NSData alloc] initWithContentsOfURL:
                    [NSURL URLWithString:@"https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50"]];

// Deserialize the response to JSON
NSDictionary *json = [[NSJSONSerialization
                       JSONObjectWithData:response
                                  options:kNilOptions
                                    error:&error] objectForKey:@"response"];

このような venues(開催地) のデータの JSON 配列がレスポンスとして返ってきます。

{
  "venues": [
    {
      "id": "4c82f252d92ea09323185072",
      "name": "Golden Gate Park",
      "contact": {
        "phone": "4152522590"
      },
      "location": {
        "lat": 37.773835608329,
        "lng": -122.41962432861,
        "postalCode": "94103",
        "cc": "US",
        "state": "California",
        "country": "United States"
      }
    }
  ]
}

JSON を Realm にインポートする方法は、いくつか存在します。カスタムメソッドを作り、手動で RLMObject のそれぞれのプロパティに配置することもできます。 この例の見どころは、直接 NSDicionary に挿入する代わりに、自動的に RLMObject を作っているところです。 これをきちんと動作させるためには、RLMObject のプロパティの構造と、JSON の key を完全に一致させておく必要があります。 もし、それらが違っている場合、そのプロパティまたは key は無視されます。以下の RLMObject の定義では、問題なく動作します。

// Contact.h
@interface Contact : RLMObject
@property NSString *phone;
@end

@implementation Contact
+ (NSString)primaryKey {
    return @"phone";
}
@end
RLM_ARRAY_TYPE(Contact)

// Location.h
@interface Location : RLMObject
@property double lat; // latitude
@property double lng; // longitude
@property NSString *postalCode;
@property NSString *cc;
@property NSString *state;
@property NSString *country;
@end

@implementation Location
@end
RLM_ARRAY_TYPE(Location)

// Venue.h
@interface Venue : RLMObject
@property NSString *id;
@property NSString *name;
@property Contact  *contact;
@property Location *location;
@end

@implementation Venue
+ (NSString)primaryKey {
    return @"id";
}
@end
RLM_ARRAY_TYPE(Venue)

結果のデータが配列として返ってくるので、[Venue createInDefaultRealmWithObject:] を毎回呼びオブジェクトを作らなければいけません。 この例は、JSON データから Venue オブジェクトを複数作り Default Realm に追加してます。

//Extract the array of venues from the response
NSArray *venues = json[@"venues"];

RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
// Save one Venue object (and dependents) for each element of the array
for (NSDictionary *venue in venues) {
    [Venue createOrUpdateInDefaultRealmWithObject:venue];
}
[realm commitWriteTransaction];

次のステップ

Realmをより深く理解する、次のステップとして、サンプルコードを用意しています。 HAPPY HACKING!! Google グループ Realm で、あなたはいつでもRealmデベロッパーと議論ができます。

開発中の機能

Realmは、現在β版としてリリースされています。バージョン1.0 に向けて、機能追加、バグの修正などを行っています。私たちは、次に導入予定のリストを作成しました。 詳しくは、GitHub issues をご覧ください。

一般的な制約

Realmは、柔軟性とパフォーマンスのバランスを上手く保つため、保存するデータに対していくつか制約があります。

  1. クラス名は、UTF-8 の 0 ~ 63バイトである必要があります。これを超過している場合、ロード時に例外が投げられます。
  2. プロパティ名は、UTF-8 の 0 ~ 63バイトである必要があります。これを超過している場合、ロード時に例外が投げられます。
  3. NSData 型のプロパティでは、16MB 以上のデータを持つことはできません。 これ以上のデータを保存しようとすると、16MB のチャンクに分割されるか、ファイルにそのまま書き込まれるかのどちらかです。プロパティの値が 16MB 以上のデータを保存しようとすると、それを実行時に例外が投げられます。
  4. NSDate 型のプロパティは、保存されると少数点以下が切り捨てられます。 +[NSDate distantFuture]+[NSDate distantPast] で作られたデータは保持できません。詳しくは、NSDateについてをご覧ください。
  5. Realm ファイルのサイズは、一つの iOS アプリに割り当てられるメモリサイズを超えてはいけません。割り当てられるメモリサイズは、デバイス毎に違い、また、どのくらいメモリのフラグメンテーションが進んでるかにも依存します。(ここについては、Open Radar の rdar://17119975 をご覧ください) これ以上のデータを保存される場合は、Realm ファイルを複数に分けご使用ください。

より詳細なNotification

Realm のデータが変更される毎に、Notification を取得することはできますが、現在は、Notification からアクション(Add, Remove, Move, Update など)を識別することができません。 この機能を近々、追加する予定です。

NSDateについて

NSDate は、一度、Realmに保存されると少数点以下は切り捨てられます。 この問題は、現在修正中です。 詳しくは、 GitHub issue #875 をご覧ください。 それまで、少数点以下の時間を正確に保存したい場合は、 NSTimeInterval(double)型 をお使いください。

RealmObjectのセッター/ゲッターのオーバーライドについて

Realm は、データベースのプロパティと直結させるためにセッター/ゲッターをオーバーライドしています。そのため、Realmモデルクラスで、プロパティのセッター/ゲッターをオーバーライドすることはできません。一時的な解決方法は、ignore properties として宣言することです。こうすることで、セッター/ゲッターのオーバーライドができるようになります。

KVOのサポートについて

現在、KVOはサポートされていませんが、独自の Notification の仕組みがあります。KVO のサポートも実装予定です: GitHub issue #601

複数プロセスからのRealmの利用

もちろん、複数のスレッドから同時にRealmファイルへアクセスすることはできますが、シングルプロセスからのみ利用可能です。 これは、主に、iOS8のExtentionsとOSXアプリケーションでの開発に影響します。複数のプロセスで同じRealmファイルを使う場合は、Realmファイルをコピーするか、もしくは、新しいRealmファイルを作成してください。 マルチプロセスサポートは、近々、追加するする予定です。

ファイルサイズと中間バージョンについて

誰もが、SQLiteを使ってデータを保存した時よりもディスクの使用容量が少なくなることを期待されることかと思います。 Realmファイルの容量が非常に大きくなった場合、Realmがオブジェクトをトラッキングするために中間データを作っているためです。 invalidate を呼ぶことで、Realmオブジェクトを解放してください。 データの一貫性を保つために、データをフェッチしたときのみバージョンがアップデートされます。 このことは、Realmからデータを読み込み、別のスレッドでそのデータの更新が行われた場合、読み込まれたデータの情報はアップデートされないということです。 そして、Realmは、この中間バージョンを管理するために、内部でのみ使用されるデータが増えていき、その結果、ディスクの使用領域が肥大化してしまうことになります。 (この余分なスペースは、今後の書き込みで再度、使われるか、圧縮されることになります。例えば、writeCopyToPath:error が呼ばれた時などです)

その代わりに、invalidate を呼ぶことで、Realmにこれまでに読んできたデータは必要なく、オブジェクトの中間バージョンは必要ないと伝え、解放してあげてください。

FAQ

Realmは、どれくらいの大きさですか?

アプリをリリースビルドすると、Realm のサイズは1MBぐらいになります。 現在、配布されているRealmは、ARM, ARM64, x86 のシュミレータに対応しているのとDebug用のシンボルが含まれてるため、著しく大きく(iOS版は、~37MB, OSX版は、~2.4MB)なっています。それらは、ビルド時に Xcode が、自動的に取り除いてくれます。

Realmをプロダクション環境で使うことはできますか?

Realmは、2012年から商業利用がされています。 ご利用される場合は、Realm の Objective‑C/SwiftAPI が、頻繁に変わるものだとお考えの上、Community Feedback を確認しながらお使いください。 機能追加、バグ修正も同様にお考えください。

Realmを使うのにお金を払わないといけませんか?

いいえ、Realm は、完全に無料です。商業利用も可能です。

どのようなビジネスプランなのですか?

すでにエンタープライズ向けの商品の販売や、周辺サービスによって収益を得ています。もし現在リリースされているものや realm-cocoa で更に必要なものがあれば、いつでもメールで、お気軽にご連絡ください。また私たちのビジネスとは関係なく、realm-cocoaはオープンに開発をつづけていき、Apache License 2.0の元にオープンソースで公開し続けます。

“tightdb” や “core” という文字をコードの中で見たのですが、これは何ですか?

TightDB というのは、C++ で実装されたストレージエンジンの名前です。現在、オープンソースではありませんが、Apache License 2.0 として公開することを検討中です。 バイナリリリースは、Realm Core (TightDB) Binary License として利用可能です。