Realm Blog

Realm Javaのセットアップ方法が変わります

by /

これまでのRealmの制限事項

Realm for Androidをリリースしてからのこの1年、ライブラリにはいくつかの制限があるにもかかわらず高い評価をいただきました。

Realm for Androidの制限事項の中でも、RealmObjectを継承したクラスに課せられる以下のような制約に対する多くのご要望をいただきました。

  • publicなフィールドを持つことができない
  • アクセッサのメソッド名が標準的なものに限られている
  • アクセッサに独自のロジックを持つことができない
  • カスタムメソッドを持つことができない
  • メソッドを持つinterfaceを実装することができない

これらの制限事項はある共通の理由に由来しています。それは、Realmがデータストアに対するzero-copyオペレーションを実現するためにproxyクラスを使用しているということです。

それでは、これらの制限事項を一つずつ見ていきましょう。

publicなフィールドを持つことができない点について

Java言語はフィールドに対するアクセスを横取りすることを可能にする言語仕様を持っていないので、フィールドに対するアクセスに対してRealmのproxyクラスが割り込むことができません。また、フィールドの情報はJavaヒープ上に保持されることが言語仕様で定められています。このことにより、フィールドへの直接アクセスを許してしまうとRealmの情報をフィールドにコピーしなければならないことになり、zero-copyではなくなってしまいます。

アクセッサのメソッド名が標準的なものに限られている点について

アノテーションプロセッサはとても強力なツールですが、いくつかの制限も存在しています。そのうちの一つに、proxy対象のクラスのフィールドやメソッドの名前は取得できるが、メソッド内の処理内容を取得できないというものがあります。そのため、Realmのproxyクラスが意図通りに動作するかは利用者が規約に従った実装を行っているかに依存します。

アクセッサに独自のロジックを持つことができない点について

これは一つ前の項目から導かれる制限です。アノテーションプロセッサはアクセッサの実装内容を取得することができないので、proxyクラスにその実装内容を取り込むことができません。もちろん単にsuper.getMyField()を呼び出すことはできますが、この呼び出しはメモリー上のデータに対する操作であり、proxyクラスが対象とするRealmのデータを対象とするものにはなりません。

カスタムメソッドを持つことができない点について

繰り返しになりますが、アノテーションプロセッサはコードの内容を取得することができないので、カスタムメソッドが実装に行っている処理を把握することができません。このようなメソッドを許してしまうと正しく動作することを保証できなくなってしまうため、Realmはそのようなメソッドを許容していません。

メソッドを持つinterfaceを実装することができない点について

カスタムメソッドを許容しないという制限により、メソッドの実装が必要なinterfaceについては許容されません。

制限事項の解消に向けて

これらの制限事項を解消する唯一の方法は、バイトコード変換を使用することです。これは、ソースコードのコンパイルで生成された.classファイルに対して加工を行うことを意味しています。どのような加工を行うかについては別の記事での解説を予定しているので、ここでは詳細については割愛します。重要なことは、Gradleプロジェクトでバイトコード変換を行うためには、Gradle pluginが必要であるということです。これはRetroLambdaHugoなどが行っている方法です。

build.gradleの記述方法の変更

これまでは、以下のように設定することでRealmを使用していました。

repositories {
    jcenter()
}

dependencies {
    compile 'io.realm:realm-android:<version>'
}

これを、以下のようにRealmのGradleプラグインを使用する方式に書き換える必要があります。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'io.realm:realm-gradle-plugin:<version>'
    }
}

apply plugin: 'realm-android'

大部分のコードはすでにbuild.gradleにすでに記述があると思うので、classpathapply plugin: 'realm-android'の行を追加してください。

なぜ今変更するのか

なぜまだバイトコード操作方式がリリースされてないこのタイミングでプラグインを提供しはじめるのでしょうか?

これにはバイトコード操作以外にも理由があるからです。

APKサイズの削減!

プラグインを使用することで、AAR形式のRealmを簡単に使用することができるようになります。また、アノテーションプロセッサのクラスがライブラリ本体から分離されます。このことは、生成されるAPKファイルにはアノテーションプロセッサのクラスが含まれなくなることを意味し、数Kバイトではありますが、サイズが削減できます。

ABI splitsを簡単に!

AAR形式のRealmを利用できるようになる事の別の利点として、これまでABI splitsを使用する際に必要であったワークアラウンドが不要になります。これにより、すべてのCPUアーキテクチャ用のネイティブライブラリが含まれた巨大なAPKで配布する必要はなくなります。

Eclipseはどうなるのでしょうか?

おそらくご存知だ思いますが、GoogleはADT plugin for Eclipseを非推奨としてすでにサポートを終了しています。そのため多くのAndroid開発者はすでにAndroid Studioへの移行を完了しており、我々が注力すべきもそちらであると考えています。 ただし、何を優先すべきかは開発者の皆様からのフィードバックによって決まります。そのため、AntやMavenのpluginに対する多くの要望が集まるようであれば、優先度ついて再検討を行う可能性があります。

Happy Coding!

この変更により、開発者の皆様により快適に開発を行っていただけるものと期待しています。ご意見やご要望がありましたらSlack(日本語)Twitter(日本語)Stack Overflow(日本語), GitHub(英語)で遠慮なくお知らせください。

Read more

Realm Java 0.87 — RxJavaサポート!

by /

Realm Javaの最新版をwebサイトMavenリポジトリでリリースしました。

このリリースでは、多くの要望をいただいていたRxJavaサポートが導入されました。

RxJava

RxJavaは、NetflixがリリースしているReactive Extensionsライブラリで、Observerパターンを拡張したものです。RxJavaはデータに対する変更を監視するとともに、様々な処理を組み合わせてデータに適用することを可能にします。

Realmが提供するObservableは、従来提供されていたRealmChangeListenerの仕組みの上に構築されています。そのため、今までRealmChangeListenerが受け取っていたコールバックと同じタイミングでObservableに対する呼び出しが行われます。

Realm realm = Realm.getDefaultInstance();
RealmResults<Person> persons = realm.where(Person.class).findAll();
Person person = persons.first();

Observable<Realm> realmObservable = realm.asObservable();
Observable<RealmResults<Person>> resultsObservable = persons.asObservable();
Observable<Person> objectObservable = person.asObservable();

Realmが提供するAPIには非同期のものもありますが、同様に利用可能です。

realm.where(Person.class).equalTo("name", "John").findAllAsync().asObservable()
  .filter(new Func1<RealmResults<Person>, Boolean>() {
      @Override
      public Boolean call(RealmResults<Person> persons) {
          // 完了していない場合は無視する
          return persons.isLoaded();
      }
  })
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // personsを表示
      }
  });

様々な例が、RxJava sample projectに収録されています。

RxJavaに関する設定

RxJavaは必須の依存ライブラリではありません。これは、Realmが自動的にRxJavaのライブラリを依存ライブラリに含めてはくれないことを意味しています。Realmを利用するアプリでどのバージョンのRxJavaを使用するか決めることができます。また、RxJavaを含めないようにすることで、メソッド数の増加を抑えることも可能です。

RxJavaを使用する場合は、build.gradleに依存ライブラリとして明示的に追加してください。

dependencies {
  compile 'io.realm:realm-android:0.87.0'
  compile 'io.reactivex:rxjava:1.1.0'
}

Observableインスタンスの生成方法をカスタマイズ可能にするために、RxObservableFactoryインタフェースが追加されています。 このカスタムファクトリは、RealmConfigurationで指定することができますが、指定されない場合はRealmObservableFactoryクラスがデフォルトのファクトリクラスとして使用されます。この場合、RxJava 1.1.*をサポートします。

RealmConfiguration config = new RealmConfiguration.Builder(context)
  .rxFactory(new MyFactory())
  .build()

Work-in-progress

Realm Java 0.87.0はRxJavaをサポートする最初のバージョンですが、RealmQueryRealmListに対するObservableサポートについては今回のバージョンでは提供されていません。これらについては将来のバージョンでサポートすることを予定しています。

また、RealmObjectRealmResultsは依然としてスレッドをまたいだ利用は行えません。

// Observableでよくあるバックグラウンドスレッドの使い方
Observable.defer(new Func0<Observable<RealmResults<Person>>>() {
    @Override
    public Observable<RealmResults<Person>> call() {
        return realm.where(Person.class).findAll().asObservable();
    }
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<RealmResults<Person>>() {
    @Override
    public void call(RealmResults<Person> persons) {
        // personsにアクセスするとスレッド制約違反でクラッシュします
    }
});

// 正しくは、Realmが提供する非同期APIを使用します
realm.where(Person.class).findAllAsync().asObservable()
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // UIにデータを表示
      }
  });

RealmObjectRealmResultsはライブなオブジェクトなので、Realmの更新に伴って最新の値に更新され続けます。これはデータの一貫性という点でとても素晴らしいことですが、RxJavaのイミュータブルなスレッドセーフオブジェクトと相性が良い設計とは相容れない部分があります。そこで、RxJavaサポートの一環としてRealm.copyFromRealm()メソッドを追加しました。このメソッドは、ライブなRealmObjectの内容をRealmから切り離された通常のオブジェクトにコピーします。この処理は処理時間、メモリのいずれの点においても高価ですが、簡単にbuffer()と組み合わせられるようになります。

// 更新される前と後のPersonを扱う例
realm.where(Person.class).findFirst().<Person>asObservable()
        .map(new Func1<Person, Person>() {
            @Override
            public Person call(Person person) {
                // Realmから切り離されたコピーを作成
                return realm.copyFromRealm(person);
            }
        })
        .buffer(2)
        .subscribe(new Action1<List<Person>>() {
            @Override
            public void call(List<Person> persons) {
                // `map`でRealmから切り離していない場合、v1とv2は両方とも最新に更新されてしまいます
                Person v1 = persons.get(0);
                Person v2 = persons.get(1);
            }
        });

このようなコピーを作成するワークアラウンドは将来的には不要になる予定です(issue 1208)。

詳細な変更点については、changelogを参照してください。


お読みいただきありがとうございます。Realmが素晴らしいアプリの実装のお役に立てれば嬉しく思います。お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realm Objective-C & Swift 0.97

by /

Realm Objective-CおよびRealm Swift 0.97をリリースしました。

このバージョンにはtvOSの正式サポート、Carthageによるビルドのサポート、Objective-C APIのGenericsの改善、クエリとパフォーマンスの改善、非推奨APIの削除などが含まれます。

tvOS 📺

Apple TVアプリケーションでRealmを利用することができるようになりました!すでに何人かの開発者の方がここ数か月の間に開発ブランチのApple TV用ビルドを使用してRealmを使用したApple TV用アプリケーションを開発し、リリースしていますが、ようやく正式にリリースすることができました!🎉

tvOSではDocumentsディレクトリへの書き込みは禁止されているので、tvOS用のフレームワークでは、デフォルトの保存先はNSCachesDirectoryに変わっています。ただし、キャッシュディレクトリ内のファイルは常にシステムによって削除される可能性があるので注意してください。そのため、tvOSにおけるRealmの利用は、復元可能なデータのキャッシュなどに利用し、消えては困る重要な(再生成できない)ユーザーデータなどを保存するのは避けることを推奨いたします。

CarthageでソースコードからRealm Swiftをビルドできるようになりました

ビルドシステムの大幅は改善により、Realm Objective-CとRealm Swiftのプロジェクトが1つに統合されました。それにより、Realm Swiftフレームワークをソースコードから(ビルド済みバイナリを使わないオプションを使って)carthage --no-use-binariesビルドできるようになりました。

暗号化Realmのデバッグ

これまで、Realmの暗号化の仕組みはMach Signal Handlingに多くを依存していました。そのことがLLDBの動作に干渉していました。

このバージョンからシグナルハンドラをまったく使わない新しい仕組みで暗号化を再実装したので、暗号化を有効にしていてもLLDBセッションにプロセスをアタッチできるようになりました。さらに、暗号化が各種のクラッシュレポーター(CrashLyticsなど)の動作に干渉することがなくなりました。そして、Mach Signalが使えないwatchOSとtvOSの両プラットフォームで暗号化が利用できる目処がたちました。watchOSとtvOSの暗号化はまだ検証中なので利用できませんが、近い将来、可能になります。

Realm Objective-CにおけるGenerics型アノテーション

Objective-C APIの可能な限りすべての場所にGenerics型指定のアノテーションを付加しました。

非互換の変更

  • 以前のバージョンで非推奨となっていたメソッドが削除されました。
  • ランループを持たないスレッドでRealmのNotificationブロックを追加しようとすると、例外が発生するように変更されました。これまでは、ランループを持たないスレッドでNotificationブロックを追加すると、例外にはなりませんが、ブロックが呼ばれることはありませんでした。

細かな改善

  • -[RLMRealm transactionWithBlock:]Realm.write(_:)のブロック(クロージャ)引数に__attribute__((noescape))/@noescape属性がつきました。
  • Key-pathを使用したほとんどのクエリについて、左辺と右辺のどちらに対しても比較演算子が使用できるようになりました。
  • RLMResultsRLMArrayでKVCコレクション演算子が使えるようになりました。
  • Swiftにおいて、+[RLMRealm sharedSchema]内で計算済みプロパティを初期化しようとするとき、デッドロックを起こさずに失敗するようになりました。

不具合の修正

  • 関連を検索条件として取得したRLMResults-[RLMRealm deleteObjects:]に渡して削除するときのパフォーマンスを改善しました。
  • Realmに保存できないオブジェクト型のプロパティを定義しようとすると例外を投げるようになりました。

お読みいただきありがとうございます。 Realm で素晴らしいアプリケーションを作りましょう!Stack OverflowGitHubTwitterでお待ちしています。

Read more

Realm Java 0.86 — 新しいMigration APIの登場!

by /

Realm Javaの最新版をwebサイトMavenリポジトリでリリースしました。

このリリースでは、従来のMigration APIを置き換える新たなMigration APIと、Migrationの際に必要となるデータ変換で使用するDynamic APIを導入しました。

Migration API

マイグレーション(モデル定義の変更にともなう移行処理)では、移行のためのロジックを記述しRealmConfigurationにセットする点に変わりはありませんが、ロジックを記述するためのAPIが刷新されました。

新しいAPIでは、DynamicRealmと呼ばれる新しい種類のRealmインスタンスを操作することでマイグレーションロジックを記述します。

DynamicRealmは、スキーマ情報を変更することを可能にした特殊なRealmですが、データの作成やクエリなどは従来のRealmと同様に行うことができます。

RealmMigration migration = new RealmMigration() {
  @Override
  public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

     // DynamicRealmからは変更可能なスキーマインスタンスを取得できます
     RealmSchema schema = realm.getSchema();

     if (oldVersion == 0) {
        // バージョン1への移行処理
        oldVersion++;
     }

     if (oldVersion == 1) {
        // バージョン2への移行処理
        oldVersion++;
     }
  }
}

// 従来通りRealmConfigurationを作成
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context)
  .schemaVersion(2)
  .migration(migration)
  .build();

クラスやフィールドは名前の文字列で参照が可能です。また、作成や変更はメソッドチェーンで記述することができます。

RealmSchema schema = realm.getSchema();

// nameとageの2つのフィールドを持ったPersonクラスを作成
schema.create("Person")
    .addField("name", String.class)
    .addField("age", int.class);

// 属性を指定してフィールドを作成
schema.get("Person")
    .addField("id", long.class, FieldAttribute.PRIMARY_KEY);

// リンクを追加
schema.get("Person")
    .addRealmObjectField("favoriteDog", schema.get("Dog"))
    .addRealmListField("dogs", schema.get("Dog"));

// マイグレーション中で新たなオブジェクトを作成することも可能
DynamicRealmObject person = realm.createObject("Person");
person.setString("name", "John");

// クラスの削除(クラスに属するオブジェクトも削除されます)
schema.remove("Person");

特定のRealmObjectクラスに対してデータの変換を記述することもできます。これはオブジェクトの型を変更する際や、フィールドの結合や分割の際に威力を発揮します。

schema.get("Person")
  .transform(new RealmObjectSchema.Function() {
    @Override
    public void apply(DynamicRealmObject obj) {
      obj.setInt("age", obj.getInt("age") + 1); // すべてのオブジェクトのageを1増やす
    }
  });

どのような操作が可能かについては、RealmSchemaRealmObjectSchemaのAPIリファレンスを参照してください。 また、migration exampleも新しいAPIに対応するように更新されています。

Dynamic API

新たに導入されるDynamic APIはマイグレーションだけでなく、通常のデータの読み書きにも使用することができます。例えばCSVからのデータの読み込みを記述するといった、コンパイル時にはモデル定義が定まらない処理を実装する際に威力を発揮します。

Dynamic Realmは、従来のRealmインスタンスを取得する際に使用していたRealmConfigurationがそのまま利用できますが、スキーマ定義、スキーマバージョン、マイグレーションロジックは無視されます。

RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// DynamicRealmでは、すべてのオブジェクトはDynamicRealmObjectです
DynamicRealmObject person = realm.createObject("Person");

// すべてのフィールドは名前の文字列でアクセスします
String name = person.getString("name");
int age = person.getInt("age");

// データベースのスキーマは依然として有効なので、スキーマに違反する操作には例外がスローされます
person.getString("I don't exist");

// クエリは従来通り使用可能です
RealmResults<DynamicRealmObject> persons = realm.where("Person").equalTo("name", "John").findAll();

DynamicRealmは柔軟性のために型安全とパフォーマンスを犠牲にしています。DynamicRealmが提供する柔軟性がどうしても必要な場合でのみ使うようにしてください。

#enumsmatter

クエリAPIでは、SortCaseを指定する際、従来のbooleanではなくenumを使うように変更されています。 この変更で、より可読性が高く型安全なコードが記述できるようになります。

realm.where(Person.class)
  .beginsWith("name", "John", Case.INSENSITIVE)
  .findAllSorted("name", Sort.ASCENDING)

変更の一覧はchangelogを参照してください。

Read more

Realm Java 0.85 — 暗号化実装方式の刷新!

by /

Realm Javaの最新版をwebサイトMavenリポジトリでリリースしました。

このリリースでは、従来のシグナルハンドラベースの暗号化実装を、すべての端末で動作する一般的な方式へ変更しています。また、新規のAPIとしてRealm.isEmpty()が追加されています。

暗号化実装の刷新

“A Look Into Realm’s Core DB Engine”(日本語未翻訳)で触れているように、 Realmの暗号実装はPOSIXシグナルハンドラに依存していました。

シグナルベースのアプローチは、ソフトウエアとしてはとてもシンプルでエレガントなものです。ところが現実のAndroidの世界では、このシグナルベースのアプローチに起因する奇妙なクラッシュがいくつか発生していました。

  • 5.0 Lollipop以降、AndroidはPlay StoreでChromium WebViewを提供し始めました。ところがこのChromium WebViewのv40には、Realmの暗号化機能とともに使用するとアプリケーションのクラッシュを引き起こすというバグがありました。詳細はこちらのissueコメントに書かれています。
  • いくつかの古い端末(HTC One X等)では、シグナルハンドラが正しく動作しません。このHTC One Xでの問題はFirefox for Androidでも発生していました。詳細はこちらのBugzillaページを参照してください。

できる限り多くの端末で暗号化機能を使用できるようにワークアラウンドを組み込みましたが、HTC One Xのように必要なAPIが正しく動作しない端末で動作させることはできませんでした。

これらの状況をふまえ、Realmの暗号化実装のアプローチを見直し全面的に書き換えることにしました。その結果本バージョン(0.85.0)以降では、Realmの暗号化実装はあらゆる端末で動作するものになりました。

すべての端末で暗号化が利用できるようになったので、RealmEncryptionNotSupportedExceptionのcatch節は不要になります。

新規API - Realm.isEmpty()

Realmが1つでもデータを含んでいるかまったく何も含んでいないかということをRealm.isEmpty()でチェックできるようになりました。

非互換の変更

RealmEncryptionNotSupportedExceptionは不要になったので、本リリースで削除されています。🎉

Realm.executeTransaction()はトランザクション内で発生したRuntimeException例外を別のRuntimeExceptionにラップすることなくそのままスローするようになりました。 発生した例外がそのままスローされるので、よりデバッグが行いやすくなります。トランザクション処理での例外発生時は、今まで通り自動的にトランザクションはロールバックされます。

RealmQuery.isNull()RealmQuery.isNotNull()について、関連をたどるクエリ(プロパティ名を.で連結した文字列)が指定されその最後の要素が他のオブジェクトへの関連である場合にスローされる例外がRealmErrorからIllegalArgumentExceptionに変更されました。

Read more

Realm Objective-C & Swift 0.96

by /

このバージョン(0.96)で読み書きを行ったRealmファイルは以前のバージョンで開くことができなくなります。既存アプリケーションに対して更新を適用する場合は特に注意してください。

Realm Objective-CおよびRealm Swift 0.96.0をリリースしました。

このバージョンにはNULLプロパティのサポート、コレクションに対する集計関数(count/min/max/sum/avg)のサポート、SwiftにおいてRealmCollectionTypeの導入とエラーハンドリングの改善、および不具合の修正が含まれます。

NULLサポート

このバージョンではすべてのプロパティの型に対して、NULL値を格納できるようになりました。

Objective-C

NSStringNSDateNSData、型のプロパティは、デフォルトでnilが代入可になります。もしnilの代入を禁止する場合は、モデルの+requiredPropertiesのクラスメソッドをオーバーライドし、nilの代入を禁止するプロパティの名前を返すようにします。 旧バージョンで作成されたファイルにアクセスした時に、+requiredPropertiesで指定したプロパティ以外は、自動的にはnil可に変換されます。

数値型のプロパティについてはNSNumberを利用することでnil可を示します。NSNumber<RLMInt>NSNumber<RLMBool>NSNumber<RLMFloat>NSNumber<RLMDouble>のように実際の数値の型を指定することができます。

@interface OptionalTypes : RLMObject
@property NSString *optionalString;
@property NSString *requiredString;
@property NSData *optionalData;
@property NSDate *optionalDate;
@property NSNumber<RLMInt> *optionalInt;
@property NSNumber<RLMBool> *optionalBool;
@property NSNumber<RLMFloat> *optionalFloat;
@property NSNumber<RLMDouble> *optionalDouble;
@end
@implementation OptionalTypes
+ (NSArray *)requiredProperties {
    return @[@"requiredString"];
}
@end

Swift

StringNSDateNSData型のプロパティは一般の型と同様にOptionalまたは非Optionalのどちらかを指定することができます。残念ながら、数値型の各種については直接Optionalを指定することをサポートすることができませんでした。(詳しくはこちら #2147)をご覧ください。そのため、数値型でnilを扱うにはRealmOptionalでラップする必要があります。

(例)

class OptionalTypes: Object {
    dynamic var string: String? = "B"
    dynamic var data: NSData? = "C".dataUsingEncoding(NSUTF8StringEncoding)
    dynamic var date: NSDate? = NSDate(timeIntervalSince1970: 10)
    let int = RealmOptional<Int>(1)
    let float = RealmOptional<Float>(2.2)
    let double = RealmOptional<Double>(3.3)
    let bool = RealmOptional<Bool>(true)
    let boolWithNilDefault = RealmOptional<Bool>(nil)
}

List型のプロパティと同様に、RealmOptional型のプロパティはletで宣言する必要があり、dynamicを付けてはいけません。RealmOptionalでラップした実際の値を取得・設定するにはvalueプロパティを使用します。

ファイルフォーマットの自動変換

このバージョンでRealmファイルを開くと、自動的に新しいファイルフォーマットに変換されます。読み込みだけで書き込みを行わなかったとしても変換は行われますので注意してください。この自動変換では下記の操作が行われます。

  1. すべてのインデックスが作り直されます。(IntとStringの型にのみ適用されます)
  2. すべてのNSStringNSDateNSData型のプロパティは+requiredPropertiesを指定しない限り、NULL代入可に変換されます。
  3. 新しいフォーマットのバージョン番号がファイルに書き込まれます。

逆に旧バージョンのRealmで作成されたファイルは新しいバージョンで読み込むことができません。この変換はファイルの書き込みを必要とするため、もしアプリケーションにRealmファイルをバンドルして読み込み専用で利用している場合は、新しいファイルフォーマットに更新して置き換える必要があります。

注意:このファイルフォーマットの変換を元に戻すことはできません。

キーパス・コレクション・クエリ(Keypath Collection Queries)

コレクションオブジェクト(RLMArray/List)に対する集計関数@count@min@max@sum@avgがサポートされました。詳しい使い方はRealmのWebサイトで公開しているNSPredicateチートシートをご覧ください。

RealmCollectionType

Realm Objective-Cでは長らくRLMArrayRLMResultsは共通のプロトコル(RLMCollection)の機能を利用することができました。このバージョンから、Swiftでも同様の機能が利用できるように拡張されました。

ListResultsクラスは共にRealmCollectionTypeプロトコルに準拠するように宣言されています。type-erased wrapperが提供されているので、Swiftであっても異なるレイアウトのオブジェクトを同じコレクションに格納することができます。

class ViewController: UIViewController {
    var collection: AnyRealmCollection

    init(collection: RealmCollectionType) {
        super.init()
        self.collection = AnyRealmCollection(collection)
    }
}

エラーハンドリングの改善

現在のRealmではトランザクションをコミットした際に、ディスクの空き状況が不足している場合、クラッシュを引き起こすことがあります。それによってデータが破損することがありませんが、ユーザ体験としては非常に良くありません。

この変更によって、commitWritecommitWriteTransactionwritetransactionWithBlockがディスクの空き容量が不足している際には、エラーを返すようになりました。

細かな改善

  • RLMRealmRealmisEmptyプロパティを追加しました。Realmに1つでもオブジェクトが保存されているかどうかを示します。

不具合の修正

  • 8MB以上16MB以下のサイズのNSDataを保存した時にアサーションに失敗することがある問題を修正しました。
  • 1対1または1対多の関連のプロパティが削除されたマイグレーションをロールバックした際にアサーションに失敗することがある問題を修正しました。
  • ファイルのオープンに失敗した際のエラーオブジェクトにパスの情報を含めるようにしました。
  • モデルのサブセットを指定してRealmをオープンした後にサブセットを指定せずに開くとクラッシュする問題を修正しました。
  • Object型のプロパティのデフォルト値を持つモデルをObject(value: ...)で作成する際のエラーを修正しました。
  • モデルのサブセットとして与えられたクラスが関連を持つとき、その関連のクラスがサブセットに含まれていないときは例外が発生するようになりました。
  • 複数のスレッドから短期間にに何回も同じRealmファイルを開いたときにクラッシュすることがある不具合を修正しました。
  • いくつかのエラーメッセージで間違った関数名を表示していた問題を修正しました。

Read more

Realm Objective-C & Swift 0.96 Beta

by /

このバージョン(0.96 Beta)で読み書きを行ったRealmファイルは以前のバージョンで開くことができなくなります。既存アプリケーションに対してテストを実施する場合は特に注意してください。

NULLサポートを有効にしたRealm Objective-CとSwiftの特別なベータ版として0.96.0-betaをリリースしました。このバージョンをインストールするにはZIPファイル(Objective-C / Swift)をダウンロード、展開してプロジェクトにコピーします。またはCocoaPod、Carthageを利用する場合は、PodfileまたはCartfileのバージョンに0.96.0-beta2を指定してインストールします。

このバージョンにはNULLプロパティのサポート、コレクションに対する集計関数(count/min/max/sum/avg)のサポート、SwiftにおいてRealmCollectionTypeの導入とエラーハンドリングの改善、および不具合の修正が含まれます。

このバージョンはベータ版のため、テスト環境でのみ利用するようにしてください。新しいファイルフォーマットと自動変換の処理について、注意深く検証を行ってきましたが、 わずかな問題が潜んでいる可能性を排除するために、開発者の皆さまからのフィードバックを求めています。

ベータテストの期間が終了すると、これら機能は正式版としてリリースされます。

NULLサポート

このバージョンではすべてのプロパティの型に対して、NULL値を格納できるようになりました。

Objective-C

NSStringNSDateNSData、型のプロパティは、デフォルトでnilが代入可になります。もしnilの代入を禁止する場合は、モデルの+requiredPropertiesのクラスメソッドをオーバーライドし、nilの代入を禁止するプロパティの名前を返すようにします。 旧バージョンで作成されたファイルにアクセスした時に、+requiredPropertiesで指定したプロパティ以外は、自動的にはnil可に変換されます。

数値型のプロパティについてはNSNumberを利用することでnil可を示します。NSNumber<RLMInt>NSNumber<RLMBool>NSNumber<RLMFloat>NSNumber<RLMDouble>のように実際の数値の型を指定することができます。

@interface OptionalTypes : RLMObject
@property NSString *optionalString;
@property NSString *requiredString;
@property NSData *optionalData;
@property NSDate *optionalDate;
@property NSNumber<RLMInt> *optionalInt;
@property NSNumber<RLMBool> *optionalBool;
@property NSNumber<RLMFloat> *optionalFloat;
@property NSNumber<RLMDouble> *optionalDouble;
@end
@implementation OptionalTypes
+ (NSArray *)requiredProperties {
    return @[@"requiredString"];
}
@end

Swift

StringNSDateNSData型のプロパティは一般の型と同様にOptionalまたは非Optionalのどちらかを指定することができます。残念ながら、数値型の各種については直接Optionalを指定することをサポートすることができませんでした。(詳しくはこちら #2147)をご覧ください。そのため、数値型でnilを扱うにはRealmOptionalでラップする必要があります。

(例)

class OptionalTypes: Object {
    dynamic var string: String? = "B"
    dynamic var data: NSData? = "C".dataUsingEncoding(NSUTF8StringEncoding)
    dynamic var date: NSDate? = NSDate(timeIntervalSince1970: 10)
    let int = RealmOptional<Int>(1)
    let float = RealmOptional<Float>(2.2)
    let double = RealmOptional<Double>(3.3)
    let bool = RealmOptional<Bool>(true)
    let boolWithNilDefault = RealmOptional<Bool>(nil)
}

List型のプロパティと同様に、RealmOptional型のプロパティはletで宣言する必要があり、dynamicを付けてはいけません。RealmOptionalでラップした実際の値を取得・設定するにはvalueプロパティを使用します。

ファイルフォーマットの自動変換

このバージョンでRealmファイルを開くと、自動的に新しいファイルフォーマットに変換されます。読み込みだけで書き込みを行わなかったとしても変換は行われますので注意してください。この自動変換では下記の操作が行われます。

  1. すべてのインデックスが作り直されます。(IntとStringの型にのみ適用されます)
  2. すべてのNSStringNSDateNSData型のプロパティは+requiredPropertiesを指定しない限り、NULL代入可に変換されます。
  3. 新しいフォーマットのバージョン番号がファイルに書き込まれます。

逆に旧バージョンのRealmで作成されたファイルは新しいバージョンで読み込むことができません。この変換はファイルの書き込みを必要とするため、もしアプリケーションにRealmファイルをバンドルして読み込み専用で利用している場合は、新しいファイルフォーマットに更新して置き換える必要があります。

注意:このファイルフォーマットの変換を元に戻すことはできません。

キーパス・コレクション・クエリ(Keypath Collection Queries)

コレクションオブジェクト(RLMArray/List)に対する集計関数@count@min@max@sum@avgがサポートされました。詳しい使い方はRealmのWebサイトで公開しているNSPredicateチートシートをご覧ください。

RealmCollectionType

Realm Objective-Cでは長らくRLMArrayRLMResultsは共通のプロトコル(RLMCollection)の機能を利用することができました。このバージョンから、Swiftでも同様の機能が利用できるように拡張されました。

ListResultsクラスは共にRealmCollectionTypeプロトコルに準拠するように宣言されています。type-erased wrapperが提供されているので、Swiftであっても異なるレイアウトのオブジェクトを同じコレクションに格納することができます。

class ViewController: UIViewController {
    var collection: AnyRealmCollection

    init(collection: RealmCollectionType) {
        super.init()
        self.collection = AnyRealmCollection(collection)
    }
}

エラーハンドリングの改善

現在のRealmではトランザクションをコミットした際に、ディスクの空き状況が不足している場合、クラッシュを引き起こすことがあります。それによってデータが破損することがありませんが、ユーザ体験としては非常に良くありません。

この変更によって、commitWritecommitWriteTransactionwritetransactionWithBlockがディスクの空き容量が不足している際には、エラーを返すようになりました。

細かな改善

  • RLMRealmRealmisEmptyプロパティを追加しました。Realmに1つでもオブジェクトが保存されているかどうかを示します。

不具合の修正

  • 8MB以上16MB以下のサイズのNSDataを保存した時にアサーションに失敗することがある問題を修正しました。
  • 1対1または1対多の関連のプロパティが削除されたマイグレーションをロールバックした際にアサーションに失敗することがある問題を修正しました。
  • ファイルのオープンに失敗した際のエラーオブジェクトにパスの情報を含めるようにしました。
  • モデルのサブセットを指定してRealmをオープンした後にサブセットを指定せずに開くとクラッシュする問題を修正しました。
  • Object型のプロパティのデフォルト値を持つモデルをObject(value: ...)で作成する際のエラーを修正しました。
  • モデルのサブセットとして与えられたクラスが関連を持つとき、その関連のクラスがサブセットに含まれていないときは例外が発生するようになりました。

最後にもう一度、このバージョン(0.96 Beta)で読み書きを行ったRealmファイルは以前のバージョンで開くことができなくなります。既存アプリケーションに対してテストを実施する場合は特に注意してください。新しいファイルフォーマットと自動変換の処理について、注意深く検証を行ってきましたが、 わずかな問題が潜んでいる可能性を排除するために、開発者の皆さまからのフィードバックを求めています。

Read more

Realm の Xcode 7, Swift 2.0, WatchOS, Bitcode のサポートについて

by /

先日 Apple から発表があり、Realm のそれらへの対応状況についてご説明いたします。ドキュメントのアップデートはもうしばらくお待ち下さい。以下がそれぞれの対応状況です。

Xcode 7

Realm Objective-C は Xcode 7 に対応しています。現在、いくつか警告が出るかと思いますが、0.94 のアップデートで改善されています。

Swift 2.0

Realm SwiftSwift 2.0 に対応しています。しかし、Apple が公式にこの Swift の新しい機能を出すまでは、手動で行ってください。CocoaPods で行う方法については、ドキュメントをご覧下さい。

(Swift 2.0 からの Realm Objective-C のブリッジもサポートされています。)

また、これからも Swift 2.0 並びに 最新の安定版の Swift (1.2) の対応も行っていきます。

watchOS 2

Realm は現在 WatchKit Extensions のサポートは行っておりますが、watchOS 2 は新しいバイナリ armv7k のスライスが必要なため、もうしばらくお待ち下さい。こちらの Pull Request #2139 がこのサポートとなります。

Bitcode

現在、Realm は Bitcode の対応はしていません。しかし、今の段階でこのことにより開発がストップするということはありません。Bitcode のサポートは watchOS 2 向けのアプリを AppStore に申請するときにアプリとフレームワークにする必要があることですが、これは9月になるまで問題になりません。また、それ以前に Bitcode のサポートをいたしますので、ご安心ください。今後 Bitcode のサポート状況はこちらの issue #2137 をご確認ください。

更新(7/3 2015): この間の記事の内容では watchOS 2のサポートが既に行われていると記述がありましたが、armv7k のサポートをする必要があったためその部分に誤りが含まれていました。申し訳ございませんでした。

Read more

9ヶ月Realm

by /

Realmは、ローンチして9ヶ月で、世界で2番目に使われるデータベースとなりました。現在では、1億台以上のデバイス上で動作しており、フォーチュン500に選ばれている企業や、銀行、医療業界の企業など様々なアプリで使われています。この度、Realm.incは、シリーズBラウンドの総額$20M(日本円: 24億円)の資金調達を実施いたしました。詳しくは、TechCrunch、VentureBeat、Business Insider の記事をご参照ください。現在、Realm inc.では、一緒に働いてくれるスタッフを募集しています。


私たちは、3年間かけて開発した携帯、タブレット、スマートウォッチで動作するモバイルデータベースのRealmを9ヶ月前にリリースしました。正直なところ、当時は開発者に受け入れてもらえるか不安もありました。他の開発者が、開発したモバイルデータベースを理解してくれるだろうか? 私たちが思うほどに、モバイルデータベースのニーズがあるのだろうか?
しかし結果的に、Realmはすぐに多くの人に使われるようになり、他にもやらなければいけないことがたくさんあったことに気付いたと言うのが正しいかと思います。

この9ヶ月間、フォーチュン500の企業や、銀行、医療業界の企業や名の知れたスタートアップなどRealmは、様々なアプリ で使われるようになりました。現在、Groupon、Chatwork、ジョブクル、Aplos、 Buzzfeed、Intuit、Rite Aid、Zynga、Coinbase、Expensify、Wahoo Fitness などにも使われています。開発者は、様々なアプリでRealmを使い、私たちは、それらのアプリのデータモデルやデータストアを扱う責任があると思っています。時にそれらが、財務データや医療データであることもあります。

これらのアプリで使われているため、全体としてRealmは世界中の1億台以上のデバイス上で動作していることになります。

私たちのプロダクトが、世界中の多くの人々の毎日の生活に影響を与えています。私たちが、開発したRealmは、スマートフォンなどが急速に普及する中、世界中のデバイス上で、他のデータベースよりも使われるようになりました。

この記事は、$20M(日本円: 20億円)のシリーズBの資金調達に関するものですが、私たちには、いつまでもこのことについて立ち止まってる時間はありません。今週は、21人のチームメンバーが、サンフランシスコのオフィスに集まり、年に2度の全員総出のチームミーティングで、日夜、私たちのユーザーについて、どのようにすればソフトウェアを改善できるか議論しています。これは、今回の資金調達が必要なかったという意味ではなく、今回の資金調達により、よりプロダクトを改善していくものだと期待してください。これからも、より一層チームを拡大し、日本、韓国、オーストラリア と世界中のサポートを充実させていき、エンタープライズ向けの製品やサービスの売上を増やしていくことを目指します。

この9か月、私たちの挑戦はスタートしたばかりです。Realmはこれからもモバイル開発者にとって、より良いデータベースを開発していきます。一緒に、挑戦してくれる方は是非、ご応募ください!!

Read more