Realm Mobile Database

非常に多くの開発者が利用し、10億を超えるアプリにインストールされています。Realm Mobile Databaseは高速で使いやすく、オープンソースソフトウェアで、完全に無料で利用できます。

データの保存はとてもシンプルで、高速に検索できます。開発時間を何週間も短縮できます。

データの保存はとてもシンプルで、高速に検索できます。開発時間を何週間も短縮できます。

Realm Mobile DatabaseはSQLiteやCore Dataの代替となるテクノロジーです。ゼロコピーを実現した設計により、Realm Mobile Databaseは他のORマッパーやSQLiteよりも高速に動作します。使い方はとても簡単で、ほんの数分で理解できます。

モバイルアプリでRealmを利用するメリット

オフラインファーストの実現

オフラインファーストの実現

アプリがオフラインであっても、オンラインのときと同じように動作させることができます。

高速な検索

高速な検索

複雑な検索条件であってもナノ秒の単位で結果が得られます。さらにデータは常に自動的に最新の状態に更新されます。

スレッドセーフ

スレッドセーフ

一つのデータに複数のスレッドからアクセスしてもクラッシュすることはありません。

クロスプラットフォーム

クロスプラットフォーム

メジャーなプラットフォームすべてで、同じデータベースが使えます。

暗号化

暗号化

データを暗号化し、安全に保存できます。暗号化と復号化は透過的に行われるので意識する必要はありません。

リアクティブアーキテクチャ

リアクティブアーキテクチャ

RealmとUIコンポーネントを接続して、データの更新に応じて自動的に画面を更新できます。

class Dog {}

Dog.schema = {
  name: 'Dog',
  properties: {
    name: 'string',
    age: 'int',
  }
};

let realm = new Realm({ schema: [Dog] });
realm.write(() => {
  realm.create('Dog', {name: 'Rex', age: 1});
});

let pups = realm.objects('Dog').filtered('age < 2');
Dog *dog = [Dog new];
dog.name = @"Rex";
dog.age = 1;

RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
  [realm addObject:dog];
}];

RLMResults<Dog *> *allDogs = [Dog allObjects];
RLMResults<Dog *> *pups = [allDogs objectsWhere:@"age < 2"];
class Dog: Object {
  dynamic var name = ""
  dynamic var age = 0
}

let dog = Dog()
dog.name = "Rex"
dog.age = 1

let realm = try! Realm()
try! realm.write {
  realm.add(dog)
}

let pups = realm.objects(Dog.self).filter("age < 2")
public class Dog extends RealmObject {
  public String name;
  public int age;
}

Dog dog = new Dog();
dog.name = "Rex";
dog.age = 1;

Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
realm.copyToRealm(dog);
realm.commitTransaction();

RealmResults<Dog> pups = realm.where(Dog.class)
                               .lessThan("age", 2)
                               .findAll();
public class Dog : RealmObject 
{
  public string Name { get; set; }
  public int Age { get; set; }
}

var realm = Realm.GetInstance();
realm.Write(() => 
{
  realm.Add(new Dog
  {
    Name = "Rex",
    Age = 1
  });
});

var pups = realm.All<Dog>().Where(d => d.Age < 2);

コードをシンプルに

RealmObjectをサブクラス化してスキーマを定義します。 RLMObjectをサブクラス化してスキーマを定義します。 スキーマを定義して、 Objectをサブクラス化してスキーマを定義します。 RealmObjectをサブクラス化してスキーマを定義します。軽量なトランザクションを用いてオブジェクトを保存し、 流れるようなインターフェースで検索できます。 検索にはNSPredicateが使えます。 簡単なクエリを使って検索できます。 検索にはNSPredicateが使えます。 LINQを使って検索できます。非常に簡単にデータを扱えます。

React Native executes JavaScript in a dedicated worker thread so common concurrency primitives are unavailable.
// Query and update the result in another thread
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];
});
// Query and update from any thread
DispatchQueue(label: "background").async {
  let realm = try! Realm()
  let theDog = realm.objects(Dog.self).filter("age == 1").first
  try! realm.write {
    theDog!.age = 3
  }
}
public void onActivityCreated(Bundle savedInstanceState) {
  realm = Realm.getDefaultInstance();
  customers = realm.where(Customer.class).findAllAsync();
  changeListener = new RealmChangeListener() {
    @Override
    public void onChange(RealmResults<Customer> results) {
      listAdapter.notifyDataSetChanged(); // Update the UI
    }
  };
  customers.addChangeListener(changeListener);
}
// Query and update from any thread
Task.Run(() =>
{
  var realm = Realm.GetInstance();
  var theDog = realm.All<Dog>().First(d => d.Age == 1);
  realm.Write(() => theDog.Age = 3);
});

マルチスレッド

別のスレッドから利用するには、新しくRealmのインスタンスを取得し、あとは同じように使うだけです。各スレッドはそれぞれ独立したデータを保持しているので、データが不整合な状態になることはありません。

let key = new Int8Array(64);  // pupulate with a secure key

let realm = new Realm({ schema: [Dog], encryptionKey: key });

let dogs = realm.objects('Dog').filtered('name CONTAINS "Fido"');
// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
(void)SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
// Use the Realm as normal
RLMResults<Dog *> *dogs = [Dog objectsInRealm:realm where:@"name contains 'Fido'"];
// Generate a random encryption key
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
  SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}

// Open the encrypted Realm file
let realm = try! Realm(configuration: Realm.Configuration(encryptionKey: key))
// Use the Realm as normal
let dogs = realm.objects(Dog.self).filter("name contains 'Fido'")
// Generate a random encryption key
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);

// Open the encrypted Realm file
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
        .encryptionKey(key)
        .build();

// Use the Realm as normal
Realm realm = Realm.getInstance(config);
RealmResults<Dog> results = realm.where(Dog.class).contains("name", "Fido").findAll();
// Generate a random encryption key
var random = new Random();
var key = new Byte[64];
rnd.NextBytes(key);
var config = new RealmConfiguration("Mine.realm");
config.EncryptionKey = key;

// Open the encrypted Realm file
var realm = Realm.GetInstance(config);
// Use the Realm as normal
var dogs = realm.All<Dog>().Where(d => d.Name.Contains("Fido"));

暗号化する

ユーザデータをAES-256を用いて安全に暗号化して保持します。金融システムなどにおいても十分は安全性を確保できます。

let realm = new Realm({ schema: [Person] });
realm.addListener((sender, name) => {
  if (name === 'change') {
    this.setState({ source: sender.objects('Dog') });
  }
});
self.notificationToken = [[Dog objectsWhere:@"age > 5"]
                            addNotificationBlock:^(RLMResults<Dog *> *results,
                                                   RLMCollectionChange *change,
                                                   NSError *error) {
  if (error) {
    // handle error
    return;
  } else if (!changes) {
    [self.tableView reloadData];
    return;
  }

  // Update table view to animate deletions, insertions and modifications
  [tableView beginUpdates];
  [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]
    withRowAnimation:UITableViewRowAnimationAutomatic];
  [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]
    withRowAnimation:UITableViewRowAnimationAutomatic];
  [tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]
    withRowAnimation:UITableViewRowAnimationAutomatic];
  [tableView endUpdates];
}];
let realm = try! Realm()
notificationToken = realm.objects(Dog.self).filter("age > 5").addNotificationBlock { changes in
  switch changes {
  case .initial:
    tableView.reloadData()
    break
  case .update(_, let deletions, let insertions, let modifications):
    // Update table view to animate deletions, insertions and modifications
    break
  case .error(let error):
    // handle error
    break
  }
}
Realm realm = Realm.getDefaultInstance();

// Query in the background
RealmResults<Dog> results = realm.where(Dog.class)
  .greaterThan("age", 5)
  .findAllAsync();

// Use ChangeListeners to be notified about updates
results.addChangeListener(new RealmChangeListener<RealmResults<Dog>() {
  @Override
  public void onChange(RealmResults<Dog> results) {
    // Update UI
  }
});

// Or RxJava
Observable<RealmResults<Dog>> obs = results.asObservable();
// To bind a ListView to results of a query
public partial class DogsPage : ContentPage
{
  internal Realm realm;
  internal IEnumerable<Dog> Entries { get; }

  public DogsPage()
  {
    InitializeComponent();
    realm = Realm.GetInstance();

    // Results from queries will automatically notify the Binding
    // and update the ListView accordingly
    Entries = realm.All<Dog>().Where(d => d.Age > 5)
    DogsList.ItemsSource = Entries;
  }

<ListView x:Name="DogsList">
  <ListView.ItemTemplate>
    <DataTemplate>
      <TextCell Text="{Binding Name}" 
        Detail="{Binding Age, StringFormat='is {0:#}'}" /> 
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

リアクティブなアプリを開発する

Realmのオブジェクトは常に自動的に更新されます。常に最新のデータを表示することができます。そうするにはデータの変更を監視し、通知を受けてUIを更新します。アプリが古いデータを表示してしまうという問題はもう起こりません。

// Authenticating the User
Realm.Sync.User.registerWithProvider('http://realm.example.co:9080', 'google', googleAccessToken, (error, user) => {
  if (!error) {
    // Opening a remote Realm
    var realm = new Realm({
      sync: {
        user: user,
        url: 'realm://realm.example.co:9080/~/userRealm',
      }
    });
    // Any changes made to this Realm will be synced across all devices!
  }
})
// Authenticating the User
[RLMSyncUser logInWithCredentials:[RLMSyncCredentials credentialsWithGoogleToken:@"google token"]
                    authServerURL:[NSURL URLWithString:@"http://realm.example.com:9080"]
                     onCompletion:^(RLMSyncUser *user, NSError *error) {
  if (user) {
    // Opening a remote Realm
    NSURL *realmURL = [NSURL URLWithString:@"realm://realm.example.com:9080/~/userRealm"];
    RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
    config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:realmURL];
    RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
    // Any changes made to this Realm will be synced across all devices!
  } else if (error) {
    // handle error
  }
}];
// Authenticating the User
SyncUser.logIn(with: .google(token: "google token"),
               server: URL(string: "http://realm.example.com:9080")!) { user, error in
  if let user = user {
    // Opening a remote Realm
    let realmURL = URL(string: "realm://realm.example.com:9080/~/userRealm")!
    let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: realmURL))
    let realm = try! Realm(configuration: config)
    // Any changes made to this Realm will be synced across all devices!
  } else if let error = error {
    // handle error
  }
}
// Authenticating the User
User user = User.login(Credentials.google("google token"),
                       "http://realm.example.com:9080/auth");
// Opening a remote Realm
String realmURL = "realm://realm.example.com:9080/~/userRealm";
SyncConfiguration config = new SyncConfiguration.Builder(user,
                                                         realmURL).build();
Realm realm = Realm.getInstance(config);
// Any changes made to this Realm will be synced across all devices!
var user = await User.LoginAsync(Credentials.Google("google token"),
                                 new Uri("http://realm.example.com:9080"));

var realmUrl = new Uri("realm://realm.example.com:9080/~/userRealm");
var config = new SyncConfiguration(user, realmUrl);

var realm = Realm.GetInstance(config);
// Any changes made to this Realm will be synced across all devices!

シームレスなデータ同期

Realm Mobile DatabaseはRealm Mobile Platformが提供するRealm Object Serverと完璧に連携します。必要な作業は、Realm Object ServerのURLを指定することだけです。それ以外はすべてこれまで通りです。データを保存すると自動的に同期されます。何もする必要はありません。

Realm JavaRealm Objective‑C、そしてRealm Swiftで利用できます。Realm React NativeとRealm Xamarinはまもなく対応予定です。

オープンソース

Realm Mobile Databaseはオープンソースソフトウェアとして開発されています。数千の開発者がGitHub上のコードを改善してくれています。さらに数百の開発者の方がRealm Mobile Databaseを利用したアドオンライブラリを公開してくれています。私たちのリポジトリは25,000個以上のスターが付いています。

モバイル開発に関する記事の更新通知を受け取る

カンファレンスの講演やブログ記事の更新情報をメールでお知らせします。
過去の記事はニュースページでご覧になれます。



フォーチュン500に常に名を連ねる企業や、革新的なスタートアップ企業、AppStore総合1位を獲得したアプリでRealmが利用されています。

Realmのセットアップはほんの数分です。無料です。