Realm Blog

13949回のcommitと6148個のissueを経てRealmがバージョン1.0になりました

2014年7月に“世界初のモバイルファーストなデータベース”としてRealmを公開して以来13949回のcommitと6148個のissueを経て、本日Realm 1.0をリリースいたします。これはRealm社およびRealmプロダクトとしてだけでなく、Realmを利用していただいているiOSやAndroid開発者にとっても大きな節目であると言えます。

リリース当初はiOSとMacの開発者向けにObjective-C版を提供するのみでしたが、その後Swift版とAndroid版が加わり、つい先日React Native版とXamarin版をリリースいたしました。その結果、現在ではすべてのメジャーなモバイルプラットフォーム上の主要言語においてRealmを使用することができます。今回のRealm 1.0リリースは2年以上におよぶ開発の集大成であり、Realmをご利用いただいている皆様のご支援なしには成し得ないものでした。改めてお礼を申し上げます。

バージョン1.0に到達する以前から、Realmはモバイルアプリ開発者の方々に広く使われています。RealmはGitHubプロジェクトへの1万2千を超えるスターをいただくとともに、10万を超える開発者の方々による数万のアプリケーションにおいて実際に利用されています。これらのアプリにはStarbucks、Twitter、Anheuser-Busch、NBCUniversal、Alibaba、eBayをはじめとする大きなユーザー数をもつ企業からリリースされているアプリが含まれています。

Realmをつかうことでアプリをより良いものにすることができる、言い換えるとこれらのアプリを開発している開発者がよりよいユーザー体験をより簡単に、より短時間で開発することができるということがこれほど多くご利用いただいている理由です。また本日のバージョン1.0のリリースによって、iOS版およびAndroid版のRealmをご利用いただいている開発者の方々は成熟と安定を手にすることができます。

これまでにどのような変更が行われてきたかについては、Java版およびObjective-CとSwift版のchangelogをご参照ください。

Realmについて

RealmはSQLite上に構築されたいわゆるORMではありません。私たちはモバイルアプリケーション開発者のために1からデータベースを開発しています。そのため単なるキーバリューストアではなく、データベースエンジンが保持するデータに動的に対応付けられたネイティブなオブジェクトを提供します。このことにより、Realmは簡潔なAPIとパフォーマンスの両立を達成しています。Realmを用いることで、複雑なデータのモデリング、オブジェクト間のリンク、高度なクエリのすべてが可能となります。

// モデルの定義はRealmObjectを拡張します
public class Dog extends RealmObject {
    private String name;
    private int age;

    // ... IDEでgetter/setterを自動生成 ...
}

public class Person extends RealmObject {
    @PrimaryKey
    private long id;
    private String name;
    private RealmList<Dog> dogs; // 1対多の関連の定義です

    public Person(long id, String name) {
        this.id = id;
        this.name = name;
    }

    // ... IDEでgetter/setterを自動生成 ...
}

// 使い方は通常のJavaオブジェクトと同様です
Dog dog = new Dog();
dog.setName("Rex");
dog.setAge(1);

// デフォルトではアプリの"files"ディレクトリにファイルを保存します
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);

// 現在のスレッドで有効なRealmインスタンスを取得します
Realm realm = Realm.getDefaultInstance();

// ageの値が2より小さいDogクラスのオブジェクトを検索して取得します
final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => まだ何もオブジェクトを保存していないので0が返ります

// トランザクションを用いてデータを保存します
realm.beginTransaction();
final Dog managedDog = realm.copyToRealm(dog); // オブジェクトを引数に渡すと保存されます
Person person = realm.createObject(Person.class); // オブジェクトの生成と保存を同時に行う例です
person.getDogs().add(managedDog);
realm.commitTransaction();

// データが更新されるとリスナーに通知が送られます
puppies.addChangeListener(new RealmChangeListener<RealmResults<Dog>>() {
    @Override
    public void onChange(RealmResults<Dog> results) {
        // クエリの結果は自動的に更新されます
        puppies.size(); // => 1
    }
});

// バックグラウンドで非同期にオブジェクトを更新する例です
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm bgRealm) {
        Dog dog = bgRealm.where(Dog.class).equals("age", 1).findFirst();
        dog.setAge(3);
    }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
      // もともとの検索結果も自動的に更新されます
      puppies.size(); // => ageの値が2より小さいオブジェクトは無くなったので、0が返ります
      managedDog.getAge();   // => このオブジェクトの値が更新されたので3が返ります
    }
});
// モデルの定義は通常のObjective‑Cのクラスを定義するときと同じです
@interface Dog : RLMObject
@property NSString *name;
@property NSData   *picture;
@property NSInteger age;
@end
@implementation Dog
@end
RLM_ARRAY_TYPE(Dog)
@interface Person : RLMObject
@property NSString             *name;
@property RLMArray<Dog *><Dog> *dogs;
@end
@implementation Person
@end

// 使い方は通常のObjective‑Cのオブジェクトと同様です
Dog *mydog = [[Dog alloc] init];
mydog.name = @"Rex";
mydog.age = 1;
mydog.picture = nil; // プロパティをNULL可/不可にすることができます
NSLog(@"Name of dog: %@", mydog.name);

// ageの値が2より小さいDogクラスのオブジェクトを検索して取得します
RLMResults<Dog *> *puppies = [Dog objectsWhere:@"age < 2"];
puppies.count; // => まだ何もオブジェクトを保存していないので0が返ります

// オブジェクトの保存は下記のように非常に簡単です
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
  [realm addObject:mydog];
}];

// 検索結果は自動的に更新されます
puppies.count; // => 1

// バックグラウンドスレッドでオブジェクトを検索し、更新する例です
dispatch_async(dispatch_queue_create("background", 0), ^{
  Dog *theDog = [[Dog objectsWhere:@"age == 1"] firstObject];
  RLMRealm *realm = [RLMRealm defaultRealm];
  [realm beginWriteTransaction];
  theDog.age = 3;
  [realm commitWriteTransaction];
});
// モデルの定義は通常のSwiftクラスを定義するときと同じです
class Dog: Object {
  dynamic var name = ""
  dynamic var age = 0
}
class Person: Object {
  dynamic var name = ""
  dynamic var picture: NSData? = nil // Optional型もサポートしています
  let dogs = List<Dog>()
}

// 使い方は通常のSwiftオブジェクトと同様です
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \(myDog.name)")

// デフォルトの設定でRealmインスタンスを取得します
let realm = try! Realm()

// ageの値が2より小さいDogクラスのオブジェクトを検索して取得します
let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => まだ何もオブジェクトを保存していないので0が返ります

// オブジェクトの保存は下記のように非常に簡単です
try! realm.write {
  realm.add(myDog)
}

// 検索結果は自動的に更新されます
puppies.count // => 1

// バックグラウンドスレッドでオブジェクトを検索し、更新する例です
dispatch_async(dispatch_queue_create("background", nil)) {
  let realm = try! Realm()
  let theDog = realm.objects(Dog).filter("age == 1").first
  try! realm.write {
    theDog!.age = 3
  }
}
// モデルとプロパティの属性を定義します
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}, // プロパティをOptional(NULL可)にする例です
  }
};

// モデルの定義を渡してRealmインスタンスを取得します
let realm = new Realm({schema: [Car, Person]});

// オブジェクトを作成して保存します
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');

// 1件のCarオブジェクトが返ります
cars.length // => 1

// もう一つCarオブジェクトを追加します
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Ford',
    model: 'Focus',
    miles: 2000,
  });
  
// 検索結果は自動的に更新されます
cars.length // => 2
// モデルの定義は通常のC#のクラスを定義するときと同じです
public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

public class Person : RealmObject
{
    public string Name { get; set; }
    public RealmList<Dog> Dogs { get; }
}

var realm = Realm.GetInstance();

// オブジェクトの検索にはLINQクエリが使えます
var puppies = realm.All<Dog>().Where(d => d.Age < 2);

puppies.Count(); // => まだ何もオブジェクトを保存していないので0が返ります

// トランザクションを用いてオブジェクトを保存・更新します
realm.Write(() =>
{
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Rex";
    mydog.Age = 1;
});

// 検索結果は自動的に更新されます
puppies.Count(); // => 1

// オブジェクトの検索にLINQクエリが使えます
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;

// 別のスレッドでオブジェクトを検索して更新する例です
new Thread(() =>
{
    var realm2 = Realm.GetInstance();

    var theDog = realm2.All<Dog>().Where(d => d.Age == 1).First();
    realm2.Write(() => theDog.Age = 3);
}).Start();

Realmを使うべき理由

使い方が簡単

Realmは使いやすさを最も重視しています。規模の大きなアプリでも数時間でRealmに対応させることができ、実装、最適化、デバッグのための時間を数週間節約できたということも珍しくありません。

速い!

Realmの使いやすさはパフォーマンスを犠牲にしません。メモリマッピング、遅延ローディング、独自のストレージエンジンなどにより、RealmはリッチなオブジェクトベースのAPIを提供しているにもかかわらず、通常SQLiteよりも高速に動作します。それぞれのユースケースでの確認を行うことをおすすめしていますが、多くの場合既存のコードをRealmに対応させることで大幅な高速化を達成できます。

クロスプラットフォーム

RealmはJavaObjective-CReact NativeSwiftXamarinをサポートしています。これらのプラットフォーム間でRealmファイルは互換性があります。また、これらすべてのプラットフォームで同じようなロジックを用いてアプリを実装することができます。デバッグの際は、.realmファイルの中身をRealm Browserを用いて確認することができます。

先進的

Realmオブジェクトはつねに最新の状態に維持され、そのことがリアクティブパターンや一方向データフローを当たり前のものにします。リンクの機能はグラフ構造のRealmオブジェクトの扱いを可能にし、先進的なクエリ言語を用いることができます。また、AES256による暗号化機構を持ち、アドオンクラスを用いることでRealm上のデータを簡単にUIに表示することができます。

信頼性

大規模なアプリや多数のユーザーがいる場合でも安心です。Realmはすでに銀行のアプリケーション、ヘルスケア分野、複雑なエンタープライズアプリ、AppleのAppストアやGoogle Playストアでナンバーワンになるようなアプリでも採用されています。

コミュニティドリブン

RealmはGitHub上でオープンに開発されています。機能はユーザーの要求によって優先順位付けされるだけでなく、コードのコントリビューションも歓迎しています。Realmのプロジェクトは1万2千を超えるスターをGitHub上で獲得し、たくさんのアプリ、プラグイン、コンポーネントが作られています。

サポート

Realmはサポートとバグフィックスを他のすべてのタスクよりも優先します。そのため、データベースに関する質問には直接その開発やメンテナンスを行っている開>発者から回答を得ることができます。 質問はSlack(日本語)Twitter(日本語)Stack Overflow(日本語)GitHub(英語)で受け付けています。

Realmの利用者について

Realmはさまざまなアプリに組み込まれ、10億人を超えるユーザーに利用されています。

Twitter

Appストアで大成功を収めたアプリがRealmを使用しています。Twitter社の動画アプリで2億人のMAUを抱えるVineが2016年からRealmを使い始めました。

Starbucks

フォーチュン500の小売り企業も主力アプリにRealmを使用しています。来店前にスマートフォンから注文し、カウンターですぐに受け取ることができます。

Alibaba

NYSEに上場している中国の巨大企業もRealmを使用しています。マーケット上での売り買いのためのアプリで使用され、昨年はおよそ30億ドルの売上を達成しています。

Budweiser

フォーチュン・グローバル500の巨大企業はTapWiserという革新的なアプリでRealmを使っています。バーや小売店がパソコン無しに素早くビールの樽や瓶を注文できるモバイルアプリです。

SAP

もしすでにConcurというアプリを使ったことがあるなら、あなたはすでにRealmユーザーかも知れません。経費精算やトラベルプランのためのアプリで、2万社に3000万人のエンドユーザーがいます。

Rite Aid

フォーチュン500の巨大小売業者もEnvisionRxというアプリで安全にお客様のデータを扱うためRealmを使っています。

ここにあげた以外にもAmazon、eBay、Google、GoPro、Walmart、Adidas、Cisco、NBCUniversal、Nike、NFLなど数多くの企業で、また日本ではAWA株式会社、日本経済新聞社、Sansan株式会社、株式会社マネーフォワード、ヤフー株式会社、LINE株式会社、株式会社サイバーエージェントなど、皆さんご存知の企業のアプリでRealmをご利用いただいています。

What’s Next

ぜひ改善に関するフィードバックをお寄せください。お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)でご相談ください。

Realm Javaの公式ドキュメントおよびGitHubリポジトリ
Realm Objective-Cの公式ドキュメントおよびGitHubリポジトリ
Realm Swiftの公式ドキュメントおよびGitHubリポジトリ
Realm React Nativeの公式ドキュメントおよびGitHubリポジトリ
Realm Xamarinの公式ドキュメントおよびGitHubリポジトリ

Realmがどのようなプロダクトに使われることになるのか、私たちはとても楽しみにしています。


Realm Team

At Realm, our mission is to help developers build better apps faster. We provide a unique set of tools and platform technologies designed to make it easy for developers to build apps with sophisticated, powerful features — things like realtime collaboration, augmented reality, live data synchronization, offline experiences, messaging, and more.

Everything we build is developed with an eye toward enabling developers for what we believe the mobile internet evolves into — an open network of billions of users and trillions of devices, and realtime interactivity across them all.

記事の更新情報を受け取る