Realm Blog

Realmが.NET Coreをサポートしました

by /

Realmが.NET Coreをサポートいたしました。C#を用いてクライアントサイドとサーバーサイドの両方でRealm Mobile Platformが利用でき、エンド・ツー・エンドのユーザー体験を構築できます。

.NET Coreとは

.NET Coreはモダンなサーバーサイド開発環境となる次世代.NETプラットフォームです。.NETデベロッパーにとってはサーバーサイドアプリの開発において次に示すようなメリットがあります。

  • クロスプラットフォーム(Linux、macOS、Windows)
  • Dockerコンテナのサポート(.NET CoreはLinux上で動作するため)
  • マイクロサービス
  • 高性能、スケーラブル
  • 1つのサーバーで異なる.NETバージョンの混在
  • .NET Coreは.NETプラットフォームの多くのコンポーネントを.NETフレームワークと共有しているのでコードの共有が容易

.NET Core support for Realm

Realm .NETコアのサポートにより、C#を使用してRealmをサーバーサイドで使用できます。 (これまではNode.jsバインディングを使用する必要がありました。)これまで.NETを専門とする開発者の方から、.NET Coreのサポートについてたくさんのご要望をいただきました。これから、モバイルアプリのサーバーサイドコンポーネントに、使い慣れたフレームワークを使用できます。例えば、.NETとRealmを使用しながらスケーラブルなDockerコンテナベースのマイクロサービスをデプロイできます。私たちは.NETコミュニティのみなさまに、新しく優れたプラットフォームをサポートできることを非常にうれしく思っています。

Realm Platformに.NET Coreサポートが追加されたことで、C#でクライアントサイドとサーバーサイドの両方を開発できます。Xamarinを使用してiOSとAndroid用のクロスプラットフォームアプリケーションを構築することもできます。また、サーバー上の.NET Coreを使用して、既存のバックエンドシステムをRealmと統合することもできます。 Realm Platformは、クライアントデバイス間のデータをシームレスに双方向で同期します。プラットフォームやサーバーを問わず、リアルタイムでアプリケーションに共通のデータレイヤーを作成できます。私たちのリアルタイム双方向同期プラットフォームにより、モダンでリアクティブなアプリケーションを簡単に作成できるようになり、差別化のためのUXの作成に専念できます。

「Realmのシンプルさと.NET Coreのスピードはとてもすばらしいです。Realmの.NET Coreサポートにより、バックエンドのマイクロサービスに最高レベルのパフォーマンスをもたらすことができることを楽しみにしています。」 - Cartasite LLC シニアソフトウェアエンジニア、Laura Thompson

Realmではデベロッパーがが一番慣れ親しんでいるプログラミング言語で開発することを簡単にしたいと考えています。.NET Coreのサポートにより、開発者のための完全なエンドツーエンドのC#エクスペリエンスを提供することができます。

同期機能を特に必要としていない場合でも、.NET CoreのRealm Databaseは強力なツールです。.NETデベロッパーは、.NET Coreを使用してクロスプラットフォームで動作するパフォーマンスの良い軽量オブジェクトデータベースを利用できます。 Windows、Linux、またはmacOS上で実行される.NETコアで構築されたアプリケーション用のスタンドアロンデータストアとしてRealmを利用できます。

Realmを使うこととても簡単です。.NET Coreを使用してXamarinアプリケーションを構築するときと同じです。詳細は、Realm Xamarinのドキュメントをご覧ください。

これからも驚くような機能を準備しています。今後のリリースでは、アプリケーションにイベント処理を統合し、レガシーデータベースシステムでフォールトトレラントなデータ転送を可能にするグローバル通知と.NET Adapter for Data Coreを開始する予定です。Realm Mobile Platform Windows上での双方向データ同期のサポートも行われています。この追加機能は、2017年後半に利用可能になります。

Read more

Realm ObjC & Swift 2.9: ユーザー名指定のアクセス権の変更、ユーザー検索、不具合の修正

by /

Realm Objective‑CおよびRealm Swift 2.9をリリースしました。 このバージョンには新機能としてユーザー名を指定してアクセス権の変更、管理者の機能としてRealm Object Serverのユーザー検索、その他多数の改善の不具合の修正を含みます。詳しくは下記をご覧ください。

ユーザー名を指定してアクセス権の変更

Realm Object Serverにユーザーを登録している場合はアクセス権の変更をユーザー名に対して行えるようになりました。

let permission = SyncPermissionValue(realmPath: realmPath,
                                     username: "alice@realm.example.org",
                                     accessLevel: .write)
user.applyPermission(permission) { error in
  // ...
}

ユーザー検索

管理者ユーザーはRealm Object Serverに登録されているユーザーの情報を取得できるようになりました。現在はユーザー名からユーザーIDを取得できます。今後、さらに取得できる情報は増える予定です。

let targetUserIdentity = "bob@realm.example.org"
adminUser.retrieveInfo(forUser: targetUserIdentity, identityProvider: .usernamePassword) { (userInfo, error) in
  guard let userInfo = userInfo else {
    return
  }
  print("The user's ROS identity is \(userInfo.identity)")
}

ログインシステムの改善

別々の認証サーバーで作られた同じIDを持つユーザーを作成することと、複数の異なるRealm Object Serverにログインできるようになりました。URLが同じサーバーの異なるエイリアスである場合、各ユーザーは引き続き別々のものとして扱われます(たとえば、それぞれが同期したRealmを独自のコピーを持っているなど)。

管理者トークンを使用してユーザーを作成する場合は、logIn()メソッドを呼び出す際に認証サーバーのURLを指定することを強く推奨します。このサーバーのURLは、将来のリリースではすべてのユーザータイプに対して必須となります。

その他の改善

  • 1つ以上のListプロパティを含むSwiftオブジェクトをインスタンス化する際のパフォーマンスが向上しました。

不具合の修正

  • ネストしたListに対してList.descriptionが正しい型を返すように修正されました。
  • Object.shouldIncludeInDefaultSchema()falseを返すオブジェクトをネストしたプロパティに’保持している場合に正しく初期化されるように修正されました。
  • RLMArrayのプロパティにselfを代入した際に要素がクリアさないように修正しました。

お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

RealmContent: たった5分でリアルタイムアップデート機能を実装できます

by /

概要

RealmContentとRealm Mobile Platformを組み合わせることで、Realm Browserアプリから直接データを編集して、iOS アプリに新しいコンテンツをすぐに追加できるようになります。

RealmContentは必要となるRealmオブジェクトを自動でアプリのスキーマに追加し、前もって設定しておいたView Controllerを使って、利用可能なコンテンツをリストし、コンテンツページをレンダリングします。

このコンポーネントのほとんどは自動で動き、柔軟なカスタマイズが可能です。土台となる部分を実装する時間と手間を省き、アプリのコンテンツを変更するために新しいバージョンのアプリを公開する必要がなくなります。

コンテンツをユーザーベースにプッシュするには、これが一番手軽で、一番早い方法です。

使い方

このダイナミックコンテンツ管理システムをiOSアプリに導入するには5つのステップがあります。

1) RealmContent のインポート

CocoaPodsや、ソースコードを直接プロジェクトに含める形で、 RealmContent を追加します。次に、RealmSwiftRealmContentの両方をViewControllerでimportします。

import RealmSwift
import RealmContent

アプリには Realm Object Server から同期されたオブジェクトのリストがあることを前提とします。 RealmContent をインポートすると、Realmによって2つのモデルが公開されます。Realmが追加する ContentPageContentElement というデフォルトのスキーマです。

複数のRealmファイルを使っている場合は、任意のObject SchemaにContentPageContentElement を追加してください。

2) ContentListDataSource の作成

利用可能なコンテンツのリストを表示するには、RealmContentContentListDataSourceを使います。

let items = ContentListDataSource(style: .sectionsByTag)

プレーンなリストには.plain を使い、tagプロパティで分割されたページを持った、セクションのあるリストには.sectionsByTagを使います。

3) DataSourceの初期化

loadContent(from:)をコールし、コンテンツのソースとして使われるRealmファイルをセットします。

items.loadContent(from: try! Realm())

また、Realm Object Serverでの変更によって、Data SourceがTableView / CollectionViewを自動的に更新させるように設定することも可能です (推奨) 。

items.updating(view: tableView)

4) 通常通り、TableView / CollectionViewのDataSourceのメソッドを実装しますが、ContentListDataSource からデータを取得します。このクラスでは、numberOfSectionsnumberOfItemsIn(section:)titleForSection(section:)itemAt(indexPath:) のようなメソッドを提供しています。

この方法で、独自のTable View DataSource / Collection View DataSourceのメソッドを実装し、必要なUIの設定やその他の処理を実行できます(詳細なサンプルコードは、RealmContentのリポジトリにあるデモアプリをご覧ください。)

5) コンテンツページの表示

ContentViewControllerを画面遷移して、コンテンツを表示します。Table View / Collection Viewのタップデリゲートメソッドや、prepareForSegue(_:)、また任意のコードを用いて遷移させます。

セルをタップしたあとに遷移させるサンプルはこちらです。

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  tableView.deselectRow(at: indexPath, animated: true)

  let item = items.itemAt(indexPath: indexPath)
  let vc = ContentViewController(page: item)

  navigationController!.pushViewController(vc, animated: true)
}

画面遷移後のView Controllerは、Table Viewを使用して、指定されたContentPageエレメントからコンテンツを動的に表示します。 コンテンツをリモートで変更すると、その変更は画面上にリアルタイムで反映されます。

動的にコンテンツを追加/編集/削除する

RealmContentを使うことで得られる一番のメリットは、表示されたコンテンツをいつでも変更できることです。すべての変更は、リアルタイムで接続しているすべてのユーザーに同期されます。(もちろん、今接続してない場合は、接続した際に同期されます。)

Realm Browserアプリを使って、新しいページの追加や削除、並び替え、コンテンツブロックの追加でさえ簡単に行うことができます。見出しやリンクのアクセントの色を変更することもできます。

Editing contentn via Realm Browser

さあ、今すぐRealmContentを試してみよう

このコンポーネントはオープンソースになっていて、ソースコードとデモアプリにはここからアクセスできます。https://github.com/realm-demos/RealmContent.

RealmContentをアプリで試してみるには、CocoaPodsを使って、Podfileにpod 'RealmContent'を加えるのが一番手軽な方法です。

新しい機能のアイディアを思いついたり、バグレポートやフィードバックをしてくださる際には、TwitterやGitHubにIssueを立ててご連絡ください。

このコンポーネントを使って、あなたがダイナミックなアプリを作るのをワクワクしながらお待ちしています!

Read more

Realm Java 3.4 - 逆方向の関連に対するクエリーとデータ同期進捗リスナーをサポート

by /

本日、Realm Java 3.4をリリースしました。今回のリリースでは、逆方向の関連に対するクエリーのための機能とデータ同期の進捗に対するリスナーのサポートが追加されました。これらについて詳しく解説していきます。

逆方向の関連に対するクエリー

Realm Java 3.1では、新機能として@LinkingObjectsアノテーションを使った逆方向の関連をベータ版としてリリースしました。今回のバージョン3.4では、この逆方向の関連をRealmListRealmObjectと同じようにクエリー中で使用できるようにしました。

public class Person extends RealmObject {
   public String name;
   public Dog dog;
}

public class Dog extends RealmObject {
   public String name;
   @LinkingObjects
   public final RealmResults<Person> owners = null;
}

// RealmListと同じように、逆方向の関連を使ったクエリー
Person owner = realm.where(Dog.class).equalTo("owners.name", "Jane").findFirst();

@LinkingObjectsに対するクエリーはRealmListに対するこれまでのリンククエリーと同じように使うことができます。また、この機能が実装されたことにより@LinkingObjects機能はベータ版としての位置づけを終え、正式版なAPIとして取り扱われます。

データ同期進捗リスナー

Realm Mobile Platformが提供するオフラインファーストのための機能により、行った変更はネットワークの状態にかかわらず即座にローカルなRealmへ適用されます。ネットワークが利用可能になると、バックグラウンドで自動的にデータの同期が行われ、サーバーと他のデバイスに変更が伝搬します。このことにより、Realm Drawのようなリアルタイムに協調動作をするアプリケーションを作ることができます。

このように自動的に行われるデータ同期ですが、場合によっては同期すべきデータがどの程度残されているかを把握したい場合もあります。たとえば、アプリの初回起動時に、表示に必要なデータの同期がどの程度で完了するかを表示するような場合や、初回起動時に限らず、同期のプログレスバーを表示することでデータ同期中であることをユーザーに伝えるような場合です。

これらを実現できるようにするため、ProgressListenerSyncSessionに対して登録することでデータ同期の進捗状況を取得できるようにAPIを拡張しました。

Realm realm = Realm.getInstance(config)
writeData(realm);
SyncSession session = SyncManager.getSession(config);

// Realmに書き込んだイメージデータのアップロードのプログレスを表示
showProgressBar();
session.addUploadProgressListener(ProgressMode.CURRENT_CHANGES, new ProgressListener() {
   @Override
   public void onChange(Progress progress) {
       if (progress.isTransferComplete()) {
           hideProgressBar();
           session.removeProgressListener(this);
       } else {
           updateProgressBar(progress.getFractionTransferred());
       }
   }
});

// サーバーからのダウンロードが発生するたびにダウンロード中を示すUIヒントを表示
session.addDownloadProgressListener(ProgressMode.INDEFINITELY, new ProgressListener() {
   @Override
   public void onChange(Progress progress) {
       showDownloadingChanges(!progress.isTransferComplete());
   }
});

注意点としては、現時点ではプログレスリスナーを登録できるのはRealmインスタンスがオープンされている間だけに限られているという点です。

本バージョンでの変更の一覧は、CHANGELOGを参照してください。


お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realmを使ってデジタルネイティブ世代に教育を提供する

by /
Thread Learning: リアルタイムのデータ同期とオフラインファーストによる協調的な自閉症教育

特別支援教育の教師の方々は、毎日、生徒の進捗に関する数千ものデータポイントを手作業でプロットします。毎日の終わりには各データをグラフ化し、定規を用いて結果を分析します。このプロセスは非常に時間がかかる大変な作業です。紙媒体を使って行うには大きな制限があります。

 

Thread Learningは教師、両親および、セラピストに対して、自閉症や特別なケアを必要とする子どもたちに教育を届ける方法を進化させています。Thread Learningの行動データ収集プラットフォームにより、介護者はiPadを使用して学生データを記録することができます。その後、プラットフォームはデータを自動的にグラフ化して分析し、品質管理を行い、両親を含む子どものケアチームのすべてのメンバーと結果を共有します。

電子データを用いることで、介護者のチームでは、すべてのメンバーの作業をより効率的に調整して、生徒のニーズに的確に対応することができます。例えば、子どもが授業中は3人の先生とスピーチについて学んでいて、同時に放課後は言語療法士、行動アナリストに教えてもらっているとします。Thread Learningは、すべてのチームメンバーのデバイス間でリアルタイムでデータを同期させます。より情報に富んだ介護者は、質の高い指導と生徒の成果の向上を提供できます。

協調的に使えるケアプラットフォームを構築する

協調的に使えるケアプラットフォームのビジョンを実現するために、Thread Learningには介護者チームがリアルタイムでコラボレーションできるデータ処理ソリューションが必要でした。共同設立者の1人は、大学院のコンピュータサイエンスのクラスでRealmと出会いました。彼はすぐに新興スタートアップでは実装することが不可能な、高度な機能を実現できることを認識しました。

“Realmは本当に開発を加速するための力になってくれました。数か月かかると考えていたことが数週間でリリースできていました。”
- Sam Raudabaugh, Co-Founder & CTO, Thread Learning

Thread Learningの開発チームは最初から自分たちのアプリをRealm Mobile Databaseを使って開発しました。そのことでMySQLや他のORMを利用した際に起こる問題に当たらずにすみました。同時に、オブジェクト指向でないデータソリューションを使うことによる技術的負債もありませんでした。

Realm Mobile Platformがリリースされると、Thread Learningの開発チームは自然にそれを使ってより堅固に、リアルタイムなデータ同期の可用性が実現できると考えました。以前は普通のREST APIを用いたデータフローが使われていました。Realmのデータ同期機能はさらに良いソリューションを提供しました。双方向データ同期により、介護者のチームはリアルタイムで同じデータセットで同時に作業できます。

“Realmをこれまで使っていたREST APIと比較してみると、どちらが良いか結果は一目瞭然でした。”
- Sam Raudabaugh, Co-Founder & CTO, Thread Learning

オフラインファースト、複雑さの排除

学校に用意されているWiFiネットワーク環境はしばしば不安定になります。そのためThread Learningではオフライン環境でも堅牢に動くことが求められます。介護者はネットワーク通信か可能かどうかにかかわらず、データを入力してアクセスできなければなりません。創設者の初期のメンターの1人は、「あまりにも大変で複雑すぎる」ために、オフライン機能は完全に避けるようにアドバイスしました。しかし、Realm Mobile Platformを用いることで、Thread Learningはデータの更新や衝突といったREST APIを用いたときに起こる典型的なネットワークの複雑性を考える必要がなくなりました。

データの損失は紙媒体でデータを管理しているときは大きな問題として長い間存在していました。そのためThread Learningは特にオフラインファーストを主なユースケースとして考えているRealmの仕組みを評価しています。Realmはクライアント側にも同じデータを持つ仕組みのため、すべてのデータはローカルデバイスに保存されていて、オフライン状態でもすべてにアクセスできます。オフライン状態でも継続的にオンラインの場合と同じ応答性の高いユーザー体験を提供できます。ネットワーク接続がオンライン状態になると、自動的にローカルの更新はRealm Object Serverに同期され、すべてのチームのデバイスに配信されます。Realmのコンフリクトの自動解決によって常にデータは最新の状態に保たれます。

“Realmのおかげで、オフライン状態の処理や、データのコンフリクトについて心配する必要はなくなりました。”
- Gregory Brill, Co-Founder & CEO, Thread Learning

Thread Learningチームがプラットフォームを進化させていく上で、Realmの新機能の開発についてオープンにしていることは、ロードマップを計画することに非常に役立っています。このことにより、成長を上手に管理し、今後5年間で米国において百万人以上の自閉症の子どもをサポートするという使命を実現することができます。

“Realmが我々を一足飛びの未来へ連れていってくれます。”
- Gregory Brill, Co-Founder & CEO, Thread Learning

Read more

Realmのサーバーレススクリプト実行環境: Realm Functionsのご紹介

by /

本日、Realmの新機能としてRealm Functionsを発表いたしました。これはモバイルの開発者にとってサーバーサイドの機能をこれまでよりも簡単に開発できるようにするものです。バックエンドの開発者がいなくてもサーバーサイドの機能を開発できるようになります。もちろんRealm Mobile Platformが提供する機能の恩恵はすべて受けることができます。サーバーサイドに新たなエンドポイントやシリアライズ、ネットワーク通信のコードを追加する必要はありません。アプリでRealmを使用し、Realmが提供するWebダッシュボードからデータの変更によって呼び出されるコードを記述します。Realm Functionsは現在はベータ版としての提供になります。本日より、すべての(エンタープライズレベルの大規模サービスから小さなサイドプロジェクトまで)開発者の方にご利用いただけます。

信頼性の高いデータストレージとリアルタイムのデータ同期は優れたアプリケーションを構築する上で重要な要素であり、これまでRealmはそれを提供してきました。さらにカスタムロジックをサーバーサイドに追加できるとなれば、無限の可能性が広がります。不正なデータの変更を検証するコードをサーバーサイドに追加したり、さまざまな他のAPIを利用したり、機械学習に必要なデータを集めることもできます。洗練されたサーバーサイドの機能を構築やモバイルアプリと統合することは難しく、基本的なストリーミングAPIでも、サーバーサイドのコードを構築するバックエンドの開発者に多くの作業を強いることになり、アプリケーションとサーバーが問題なくデータを通信できるように多くの注意とメンテナンスが必要になります。

仕事を生み出すコードではなく、仕事をするコードを書く

Realmは優れたアプリを作る際に障害となるものをすべて取り除くことに深く注力しています。これまではデータに関わる問題を解決することを主に展開していました。アプリが取り扱うデータを非常に簡単に保存できるようにし、同期し、変更に対して容易に反応することができるようになりました。これからはRealm Functionsによって、アプリ開発はただ必要な処理を書くことに集中できるようになり、より重要な機能の開発に力を注ぐことができます。

Realmの提供するWeb開発環境では、必要な処理を実行するためのJavaScriptを記述するだけで、すぐに結果得ることができます。機能を追加してもアプリケーションに脆弱性が発生したり、メンテナンスが必要となることはありません。Realm Mobile Platformがこれまで提供してきたように、クライアントにデータを送信するためにシリアライゼーションやネットワーク通信のコードを書く必要はありません。

Realm Functionsを使ってみる

とても簡単だということを示すために、Realm Tasksのデモアプリにおもしろい機能を追加しました。タスクのタイトルに時間か日にちを表す言葉が入っていると、自然言語処理APIを通知で、タスクの期限を自動的に設定します。その機能はたった数行のコードで実現されています。下記に示すビデオをご覧ください。そしてぜひご自身で試してみてください。

 

この機能を実際に使ってみるにはRealm Tasksのリポジトリからコードをダウンロードし、下記の説明にしたがって、Realm Mobile PlatformとRealm Functionsをセットアップしてください。Realm Functionsのドキュメンテーションはこちらです。

Realm Functionsはサーバーサイドの処理をシンプルなWebインターフェースから簡単に書くことができる、非常に強力な手段です。Realm Mobile Platformをご利用の方は、本日より誰でもRealm Functionsを利用できます。現在はベータ版として、Developer Editionをご利用の方には3つまで同時に実行可能なRealm Functionsを無料で利用していただけます。ProおよびEnterprise Editionをご利用の場合は実装できるRealm Functionsの数に制限はありません。 Realm Functionsを使用してぜひすばらしいアプリを作ってください!

Read more

RealmがUniversal Windows Platform (UWP)に対応しました

by /

本日より、.NETコミュニティからもっとも期待されていた機能であるUniversal Windows Platform (UWP)版のRealm Mobile Databaseの提供を開始します。UWPへの対応はマイクロソフト製プラットフォームをサポートする上でもっとも重要な部分です。これまでRealmは長い間Xamarinのモバイル環境を完全にサポートしており、数か月前にWin32のサポートを追加しました。その数週間後、Azure Marketplaceへの対応を発表し、誰でもわずか数クリックでRealm Mobile Platformを利用していただけるようになりました。このことにより、.NETコミュニティにおけるRealmデベロッパーの割合は、この2四半期ではもっとも急速に増加しました。

RealmがUWPに対応したことで、ワールドクラスのオブジェクトデータベースをすべての主要なモバイルプラットフォーム—iOS、Android、そしてWindows 10が動くすべてのデバイス—にて単一のデータモデルと、ViewModelレイヤーとして利用できるようになりました。Xamarinデベロッパーにとっては、UWPがサポートされたことで、すべてのプラットフォームにおいて、Xamarin.Forms(これまではiOS、Android、本日よりUWP)が利用可能となります。RealmとXamarin.Formsを利用することで、単一のコードベースで主要なすべてのモバイルプラットフォームに対応するアプリを開発できます。

RealmはUWPサポートによりすべての主要なモバイルプラットフォーム—iOS、Android、Windows 10—にて単一の永続レイヤーとViewModelレイヤーを使用できるようになりました。.NETのRealmコミュニティの強みとMicrosoftのモバイルイノベーションが組み合わさることで、.NET製品の拡大に引き続き力を注いでいきます。.NETコミュニティのための新しい、そしてすばらしいツールを共有することをとても光栄に思っています。Realmを用いて優れたソフトウェアが開発されることを楽しみにしてます。ドキュメントのリンクをクリックするのと同じくらい簡単です!

Read more

Realm ObjC & Swift 2.7: Permission APIの刷新、再接続の速度を改善、パスワード変更、不具合の修正

by /

Realm Objective‑CおよびRealm Swift 2.7をリリースしました。 このバージョンには新機能としてPermission APIの刷新、パスワード変更機能の追加、再接続の速度を改善、不具合の修正を含みます。詳しくは下記をご覧ください。

Permission APIの改善

同期されたRealmに対するアクセス権の変更APIをコールバックベースに変更しました。

新しいAPIは将来的に既存のRealmオブジェクトベースのAPIから置き換えられます。

例えば、これまでの方法ではアクセス権を付加、あるいは削除するときは、{RLM}SyncPermissionChangeオブジェクトを作成し、ユーザーのmanagementRealm()に保存しました。

そして、オブジェクトの変更を監視し、操作が成功したかどうかを確かめます。具体的には下記のようにします。

let permissionChange = SyncPermissionChange(realmURL: realmURL,    // The remote Realm URL on which to apply the changes
                                            userID: anotherUserID, // The user ID for which these permission changes should be applied
                                            mayRead: true,         // Grant read access
                                            mayWrite: true,        // Grant write access
                                            mayManage: false)      // Grant management access

let managementRealm = try! user.managementRealm()
try! managementRealm.write {
  managementRealm.add(permissionChange)
}
// Wait for server response
token = permissionChange.addNotificationBlock { _ in
  switch permissionChange.status {
  case .notProcessed: break // handle case
  case .success: break // handle case
  case .error: break // handle case
  }
  print(permissionChange.statusMessage) // contains error or informational message
}

Retrieving permissions used a separate Realm (SyncUser.permissionRealm()) and a separate model type (SyncPermission).

let permissionRealm = try! user.permissionRealm()
let permissions = permissionRealm.objects(SyncPermission.self)
token = permissions.addNotificationBlock { _ in
  // permissions updated
}

上記のAPIはこのバージョンから非推奨となり、新しいAPIに置き換えられます。新しいAPIではコールバックベースになり、内部でRealmを使っているという仕組みを気にしなくても使えます。

新しいAPIでは{RLM}SyncPermissionValueをアクセス権の変更に使用します。

// Applying permissions
let permission = SyncPermissionValue(realmPath: realmPath,
                                     userID: anotherUserID,
                                     accessLevel: .write)
user.applyPermission(permission) { error in
  if let error = error {
    // handle error
    return
  }
  // success!
}
// Retrieving permissions
user.retrievePermissions { permissions, error in
  if let error = error {
    // handle error
    return
  }
  // success! access permissions
}

詳しくはドキュメントのアクセスコントロールのセクションをご覧ください。

このAPIはRealm Object Server 1.1.0以上が必要です。

再接続の速度を改善

このバージョンではReachabilityフレームワークの機能を利用して、ネットワークの状態を監視しています。それにより、オフライン状態からオンラインに復帰した際の再接続の速度が即座に行われるようになります。 これまでは再接続するまでの時間はオフラインになってからの経過時間に依存していました。

パスワードの変更

-[RLMSyncUser changePassword:completion:] APIを利用してユーザーのパスワードを変更できるようになりました。このAPIの利用はRealm Object Server 1.4.0以上が必要です。

その他の改善

  • {RLM}SyncConfigurationに新しくenableSSLValidationプロパティを追加しました。(Swiftではデフォルト値として false が与えられています。)サーバーごとのSSLバリデーションを有効にします。
  • 同期されたRealmとRealm Object Server間で16MB以上のトランザクションが可能になりました。

不具合の修正

  • Swiftで定義したモデルクラスの名前を@objc()アトリビュートによって変更できるようになりました。(e.g. @objc(Foo) class SwiftFoo: Object {}
  • マイグレーション中にオブジェクトを削除した際に、-[RLMMigration enumerateObjects:block:]が正しくないoldObjectを返す問題を修正しました。
  • 読み取り権限しかないユーザーがRealmをRealm.asyncOpen(...)を使って開こうとすると失敗しる問題を修正しました。
  • KVCを使ってListプロパティにnilを設定しようとした際の挙動をRLMArrayプロパティの挙動と合わせました。
  • 同期されたRealmを使用している際に発生する!m_awaiting_pongアサーションエラーを修正しました。
  • 大文字小文字を区別しない検索を大文字小文字の区別のない文字(数値や記号)によって構成され、かつ非常に長い文字列に対して行うと非常に速度が低下する問題を修正しました。

iOS 7 Support

iOS 7のサポートは可能な限り続けますが、近い将来、iOS 8以降のみのサポートにな予定です。 release.


お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realm ObjC & Swift 2.6: 非同期にRealmを開くオプション・起動時に自動コンパクションの追加、不具合の修正

by /

Realm Objective‑CおよびRealm Swift 2.6をリリースしました。 このバージョンには新機能として非同期にRealmを開くオプション、同期されたRealmを扱う際のユーザーオブジェクトに管理者(Admin)を示すプロパティの追加、起動時に自動的にファイルサイズのコンパクションを実行する設定の追加、不具合の修正を含みます。詳しくは下記をご覧ください。

非同期にRealmファイルを開く

このリリースでは新しいAPIとして、指定したディスパッチキューで非同期にRealmファイルを開くオプションを追加しました。

この機能によりRealmファイルを開き、アクセス可能になるまでに必要な処理はすべて指定したディスパッチキューのバックグラウンドスレッドで実行されます。時間のかかるマイグレーション処理が行われる場合などに有効です。 例:

let config = Realm.Configuration(schemaVersion: 1, migrationBlock: { migration, oldSchemaVersion in
  // 時間のかかる可能性があるマイグレーション処理
})
Realm.asyncOpen(configuration: config) { realm, error in
  if let realm = realm {
    // Realm successfully opened, with migration applied on background thread
  } else if let error = error {
    // Handle error that occurred while opening the Realm.
  }
}

さらに、同期されたRealmの場合は未ダウンロードのコンテンツをローカルにすべてダウンロードするところまで行われます。

デフォルトの挙動では同期されたRealmはオフラインであってもすぐに利用可能になり、未ダウンロードのコンテンツはそのあと逐次ダウンロードされることになります。しかし、ある種のアプリにおいては最初に最新の情報を表示する必要があることもあります。そのような状況に対応するためにasyncOpenはリモートコンテンツのダウンロードを待つために使うこともできます。 例 :

let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: realmURL))
Realm.asyncOpen(configuration: config) { realm, error in
  if let realm = realm {
    // Realm successfully opened, with all remote data available
  } else if let error = error {
    // Handle error that occurred while opening or downloading the contents of the Realm.
  }
}

UserオブジェクトにisAdminプロパティを追加

Realm Object Serverにおける管理者ユーザーを示すisAdminプロパティを{RLM}SyncUserに追加しました。

このプロパティを使うと、ユーザーがRealm Object Serverの管理者権限を持っているかどうかがわかるので、任意の同期されたRealmにアクセスするコードを書くことが非常に簡単になります。

For example:

SyncUser.logIn(with: .usernamePassword(username: "admin", password: "💯secure"),
               server: serverURL) { user, error in
  if let user = user {
    // can now open a synchronized Realm with this user
    // true if the user is an administrator on the ROS instance
    user.isAdmin == true
  } else if let error = error {
    // handle error
  }
}

起動時に自動的にコンパクションを実行

現状のRealmのアーキテクチャではファイルサイズは増加するのみで減少することはありません。パフォーマンスと並列処理の安全性を両立するためです。なぜRealmがそのような方針をとっているのか、詳しくはドキュメントのマルチスレッドのセクションをご覧ください。

さらに、ファイルサイズを拡張させるのはコストの高いシステムコールを使用するので、それが頻繁に呼ばれることを避けるためにRealmは実行時にファイルサイズを縮小することはしません。その代わり、空き領域は自動的に再利用されます。

しかし、それでもいくつかのケースにおいては、Realmファイルが無視できない大きさに増加することがありました。そのため、このバージョンではshouldCompactOnLaunchというブロックのプロパティをRealmの設定オプションに追加しました。指定するとRealmを最初に開く際に条件に応じてコンパクションを自動的に実行できるようになります。

例:

let config = Realm.Configuration(shouldCompactOnLaunch: { totalBytes, usedBytes in
  // totalBytesはディスク上のRealmファイルの容量を示します。 (使用領域 + 空き領域)
  // usedBytesは実際に使用している領域の容量を示します。

  // ファイルサイズが100MB以上、かつ使用領域が全体の50%より少ない場合にコンパクションを実行
  let oneHundredMB = 100 * 1024 * 1024
  return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
})
do {
  // Realm is compacted on the first open if the configuration block conditions were met.
  let realm = try Realm(configuration: config)
} catch {
  // handle error compacting or opening Realm
}

具体的な実装は、一度Realmファイルのコンテンツをすべて読み込み、別の場所に最適化した状態で書き込みます。そのあと、元のファイルと置き換えるという動作になります。そのため、コンパクションはデータ容量によっては負荷のかかる処理になります。

そのため、コンパクションを実行するタイミングについてはアプリケーションに応じて慎重に決定することを推奨いたします。

別のプロセスからアクセスがあった場合は、条件を満たしていたとしてもコンパクションは実行されません。それはRealmファイルが別のプロセスからアクセスされている状態でコンパクションを安全に実行することはできないためです。

shouldCompactOnLaunchプロパティは同期されたRealmではサポートされません。コンパクションはトランザクションログを破棄してしまうためです。Realmの同期にはトランザクションログを保持する必要があるからです。

その他の改善

  • インデックス付きの文字列に対する大文字小文字を無視した検索を高速化。
  • RLMResultsの集計関数をRLMArrayに追加。
  • 未保存のListオブジェクトでも集計関数が利用可能に。

iOS 7 Support

iOS 7のサポートは可能な限り続けますが、近い将来、iOS 8以降のみのサポートにな予定です。 release.


お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realm Java 3.1: オブジェクト通知、バックアップからの復元、逆向きの関連

by /

本バージョンではRealmファイルのフォーマットが変更されているため、以前のバージョンのRealmから開くことはできなくなります。以前のバージョンで作成したRealmファイルは、このバージョンでオープンする際に自動的にフォーマットが更新されます。既存のアプリを更新する際は十分ご注意ください。

Realm Java 3.1をリリースしました。このリリースには、単一のオブジェクトに対する詳細な通知、同期しているRealmのデータをサーバー上で復元を行った際の回復手段の提供、関連を逆方向にたどる機能が含まれます。それぞれについて説明します。

単一のオブジェクトに対する詳細な通知

Realm Java 3.0では、コレクションに対する詳細な通知機能をリリースしました。3.1では、この詳細な通知機能を単一のオブジェクトに対しても提供します。この機能のために、RealmObjectChangeListenerインターフェースとObjectChangeSetクラスを追加しました。

Person p = realm.where(Person.class).findFirst();
p.addChangeListener(new RealmObjectChangeListener<Person>() {
   @Override
   public void onChange(Person person, ObjectChangeSet changeSet) {
       if (changeSet.isDeleted()) {
           hide(); // オブジェクトが削除された
       } else {
           // どのフィールドに変更があったかがわかるため、UIを部分的に更新することも可能です
           if (changeSet.isFieldChanged("name")) {
               updateName(person.getName());
           }
       }
   }
});

これまで提供されていた単一オブジェクトの通知機能では、実際にはそのオブジェクトのどのフィールドも更新されていないのに通知を発生させてしまうことがありました。この点も3.1で修正されています。つまり、単一のオブジェクトに対する通知は、従来の通知機能、新規に追加された詳細な通知機能にかかわらずそのオブジェクトに実際に変更がなされた場合のみ通知が呼ばれるようになります。

バックアップを復元した際の回復手段の提供

サーバー側で問題が発生した場合、正常な状態に回復させるための備えが必要です。そのため、Realm Mobile Platformでは数か月前にデータのバックアップ機能を提供しました。通常の状態では、Realmの同期エンジンは変更操作の内容のみをやり取りします。変更内容を各クライアントが受け取ったことが確認されると、サーバー上では変更ログを処分することでディスクの効率的な利用と高速化を実現します⚡️。

さらに、Realmはオフラインファーストなデータベースなので、仮に何らかの理由でサーバーがダウンしたとしてもすべてのローカルデータは依然として利用可能です。

しかし、サーバー上でバックアップからの復元が行われると、クライアントはセッションエラーハンドラーを通じて”client reset”エラーを受け取ることになります。バックアップからの復元が発生した場合であってもデータベースを使い続けることはできますが、バックアップ時点以降の変更は破棄されます。

“client reset”エラーを受け取った際、必要に応じてユーザーに状況を報告してください。ClientResetRequiredErrorクラスはその時点でのローカルのRealmデータベースの情報や、新たにサーバーからバックアップ時点のデータを取得し直すためのメソッドを提供しています。

もし直ちに”client reset”に対応しない場合は、次回のアプリ起動後の最初のデータベースアクセスの際に自動的にローカルのデータベースが削除され、バックアップ時点のデータが再ダウンロードされます。

final SyncConfiguration config = new SyncConfiguration.Builder(user , url)
       .errorHandler(new SyncSession.ErrorHandler() {
           @Override
           public void onError(SyncSession session, ObjectServerError error) {
               if (error.getErrorCode() == ErrorCode.CLIENT_RESET) {
                   ClientResetRequiredError err = (ClientResetRequiredError) error;
                   closeRealm();
                   err.executeClientReset(); // Manually do the reset
                   err.getBackupFile(); // Reference to backed up file
               } else {
                   // Handle other errors
               }
           }
       })
       .build();

逆方向の関連(BETA)

Realmの提供する関連は、双方向のものです。つまり、PersonからDogへの関連をもつ場合、Realmは自動的に反対方向の関連も作成され、自動的に更新が行われます。

通常、この逆方向の関連はモデルクラス上では隠蔽されています。Realm Java 3.1では、@LinkingObjectsアノテーションを導入することでこの逆方向の関連をモデル上で扱えるようにしました。

以下の手順により逆方向の関連を定義します。

型パラメーターに関連元となるオブジェクトのクラスを指定したRealmResults型のフィールドを追加します。フィールドはfinalであることが必要です。つぎに、そのフィールドに@LinkingObjectsアノテーションを付与します。このアノテーションには、関連元のクラスのどのフィールドの関連を使用するかを示すため、関連元クラスので関連フィールドの名前をパラメーターとして指定します。Realmは自動的にこのフィールドにRealmResultsオブジェクトをセットします。アンマネージドオブジェクトは逆方向の関連をサポートしません。

以下の例を見てください。

public class Person extends RealmObject {
   public String name;
   public int age;
   public Dog dog;
}  

public class Dog extends RealmObject {
   public String name;

   @LinkingObjects("dog")
   public final RealmResults<Person> owners = null;
}

// Use the inverse relationship 
Dog dog = realm.where(Dog.class).findFirst();
dog.owners.size(); // Find number of owners
dog.owners.first(); // Get a reference to the owner of the dog

逆方向の関連が真に威力を発揮するのはこの関連をクエリで利用できるようになってからです現時点ではまだ利用可能ではありませんが、次のリリースに含められるように準備を進めています

逆方向の関連を扱うクエリが未提供であるため、@LinkingObjectsアノテーションには@Betaアノテーションが付与されています。みなさんからのフィードバックをお待ちしています。

ファイルフォーマット変更

3.1では、Realmのデータベースファイルフォーマットが変更されています。以前のRealmファイルはオープンされる際に自動的にフォーマットの更新が行われます。新しいフォーマットのファイルは、以前のバージョンのクライアントアプリからはひらくことができなくなります。

完全な変更内容のリストは、CHANGELOGを参照してください。


お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realm ObjC & Swift 2.5: クエリの改善とSwift 3.1の対応、不具合の修正

by /

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

Realm Objective‑CおよびRealm Swift 2.5をリリースしました。 このバージョンには新機能としてダイアクリティカルマークを無視した検索、ネストしたオブジェクトに対するNULLの比較、不具合の修正を含みます。詳しくは下記をご覧ください。

Swift 3.0、3.0.1、3.0.2、3.1のバイナリが含まれます。

同期されたRealmを利用する場合は、Realm Object Server 1.3.0以上が必要になります。

ダイアクリティカルマークを無視して検索する

文字列比較のオプションに[d]を指定してダイアクリティカルマークの違いを無視して検索することができます。このオプションは大文字・小文字を区別せずに検索するオプション([c])と同時に使用することができます。

例:

  • ==[d] 'u'üと一致します。
  • BEGINSWITH[d] 'e'étoileと一致します。
  • CONTAINS[d] 'n'Piñataと一致します。
  • ENDSWITH[d] 's'diaçrïtičşと一致します。
  • ENDSWITH[cd] 'É'caféと一致します。

注意: LIKE検索にはこのオプションは使用できません。

ネストしたオブジェクトについてNULL比較が可能に

複数階層のオブジェクトについてNULLかどうかの比較ができるようになりました。 例:

class CircleObject: Object {
  dynamic var data = ""
  dynamic var next: CircleObject?
}
let realm = try Realm()
realm.objects(CircleObject.self).filter("next.next != nil AND next.next.next = nil")

その他の改善

  • RLMRealmを直接インスタンス化することは許されていないので、[[RLMRealm alloc] init]unavailableにし、利用するとコンパイルエラーになるようにしました。
  • プライマリキーに指定できない型をプライマリキーに指定している際のエラーメッセージにクラス名を含めるようにしました。

Bug Fixes

  • 同期によってスキーマがマージされた際に、不正なカラムのアサーションエラーが発生していた問題を修正しました。
  • 同期されたRealmを開く際に何もしないトランザクションが使われていたのを修正しました。
  • 同期されたRealmにおいて暗号化をサポートしました。
  • 16MBに近いサイズのデータを{NS}Data型のプロパティに格納した際にクラッシュする問題を修正しました。
  • {NS}Data型のプロパティにアクセスした際に、誤ってnilを返す問題を修正しました。
  • トランザクションを開始した際にRealmのバージョンが固定されることにより、ファイルサイズが肥大するのを修正しました。
  • 非常に大きなサイズのRLMArray/List型のプロパティを保存しようとした際にアサーションエラーが発生するのを修正しました。
  • 同期されたRealmに不正な差分データが同期され、BadTransactLog例外が発生するのを修正しました。
  • RLMArray/Listに通知を設定している場合、削除後に変更するとアサーションエラーが発生するのを修正しました。

Xcode 8.3を使う場合の注意

通常のリリースではObjective‑Cのバイナリは最新のXcodeを利用してビルドしていました。現在の最新版はXcode 8.3です。しかし、Xcode 8.3が生成するバイナリには非常に大きなビットコードが含まれ、以前のバージョンと比べて4倍以上のサイズになってしまいました(rdar://31302382)。そのためiOS版のRealm Objective-Cフレームワークは通常の55MBほどのサイズから158MBになってしまいました。そのためObjective-Cフレームワークは引き続きXcode 8.2を利用してビルドすることにしました。Xcode 8.3でビルドしたフレームワークを利用するにはソースコードからビルドしてください。App Storeから配信される際にビットコードは削除されるので、エンドユーザーにはバイナリサイズによる影響はありません。


お読みいただきありがとうございます。 Realmで素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

PostgreSQLをリアルタイムに同期する

by /

PostgreSQLはもっとも強力で人気があるデータベースの1つです。最近では本当にあらゆる企業で利用されています。PostgreSQLはどんな使い方でもたいていの場合マッチしますが、最近求められるリアルタイムで、リアクティブなモバイルアプリから利用するには少し問題がありました。PostgreSQLは非常に高速ですが、リアルタイムでリアクティブなモバイルアプリに最適化した設計ではありません。

そのためのソリューションとして、RealmのPostgreSQL Data Connectorをご紹介します。Data Connectorは、Realm Mobile PlatformとPostgreSQLの双方向のシンプルなブリッジを作成します。それにより、PostgreSQLの耐障害性と安定性を保ったまま、モバイルアプリに適したリアルタイム性をもたらします。モバイルアプリとのリアルタイムな同期は完全に自動的に行われます。つまり、モバイルクライアントに発生した変更は自動的にPostgreSQLにもリアルタイムに反映され、PostgreSQLの変更はリアルタイムにモバイルアプリに同期されます。非常に簡単にPostgreSQLとRealmを用いたリアクティブなモバイルアプリを開発できます。

REST APIを置き換えるRealm Mobile Platform

Realm Mobile Platformはモバイルアプリ開発で用いられているREST APIを用いた手法を置き換えます。Realmが提供する同期の仕組みを利用すれば、データの変更をわざわざネットワーク通信を使って何度もやりとりする必要はありません。Realmが自動的にリアルタイムの同期を使って、ネイティブオブジェクトをやりとりするので、もうJSONを使ってAPIと通信する必要ななくなるのです。Realm Object Serverが提供するAPIとの連携機能を利用すれば、データの取得・更新にAPIを呼び出す必要は無くなり、複雑でエラー処理が難しいAPI通信の処理を大幅に減らすことができます。

PostgreSQL Data Connectorのデモ

下記に示すのはPostgreSQLとRealm Mobile Platformがどのように連携するのかを示すビデオです。

例として架空のDVDレンタルの会社を想定しています。モバイルアプリから在庫情報を変更すると、その変更は即座にPostgreSQLにも同期されます。とてもシンプルですが、Realmを使うことでアプリケーション開発が非常に単純になることを表しています。

  • RealmはモバイルアプリとPostgreSQLを自動的かつリアルタイムな同期機能によって連携させます。 *

Realmがアプリケーション開発を劇的に変えることを示すことができましたか?ぜひGithubからサンプルをご覧ください。上記のDVDレンタル管理アプリのコードと、Realm Mobile PlatformとPostgreSQL、モバイルアプリがどのように連携するのかの仕組みを解説しています。

Read more

Realm Java 3.0: コレクション通知、スナップショット、関連によるソート

by /

本日、Realm Java 3.0をリリースしました。このリリースでは、関連をつかった RealmResults のソートや、RealmResultsRealmList などのコレクションが更新された際の通知で更新内容の詳細(追加/削除/更新)を受け取ることができる機能を追加しています。これらの機能によりRealmが提供するライブなコレクションの活用の幅が大きく広がります。

コレクションの詳細な変更通知(Fine-grained Collection Notification)

これまでRealmでは、RealmRealmObjectRealmResultsクラスに対してRealmChangeListenerインターフェイスをつかった通知機能を提供してきました。このインターフェイスには一つ大きな制限があります。それは、呼び出されることにより 何か が変わったということはわかるのですが、何がどのように変わったかについてはわからないという点です。変更があったことがわかれば十分という場合も多いのですが、もっと詳しい情報が必要になるケースもあります。たとえば、RecyclerViewで変更内容に応じたアニメーションを行いたい場合などです。これまでは、realm.copyFromRealm()とAndroid Support LibraryのDiffUtilを組み合わせることで実現することもできましたが、望ましいものではありませんでした。

本リリースでは、コレクションの詳細な変更通知(Fine-grained Collection Notification)機能を追加しました。RealmResultsに登録するための新たなインターフェイスにより実現されるもので、どの要素が追加、削除、変更がされたかについての情報を受け取ることができます。これにより、実際に変更があった要素についてのみ更新を行うことができるようになります。

併せて、Android AdaptersライブラリについてもRealmRecyclerViewAdapterが詳細な変更通知を利用するように更新し、スムーズなアニメーションを行うようになっています。次の動画で、実際にどのような違いが出るかを見ることができます。

private final OrderedRealmCollectionChangeListener<RealmResults<Person>> changeListener = new OrderedRealmCollectionChangeListener() {
    @Override
    public void onChange(RealmResults<Person> collection, OrderedCollectionChangeSet changeSet) {
        // `null`  means the async query returns the first time.
        if (changeSet == null) {
            notifyDataSetChanged();
            return;
        }
        // For deletions, the adapter has to be notified in reverse order.
        OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
        for (int i = deletions.length - 1; i >= 0; i--) {
            OrderedCollectionChangeSet.Range range = deletions[i];
            notifyItemRangeRemoved(range.startIndex, range.length);
        }

        OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
        for (OrderedCollectionChangeSet.Range range : insertions) {
            notifyItemRangeInserted(range.startIndex, range.length);
        }

        OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
        for (OrderedCollectionChangeSet.Range range : modifications) {
            notifyItemRangeChanged(range.startIndex, range.length);
        }
    }
};

コレクションの詳細な変更通知は、従来の変更通知のためのリスナーと同じように動作します。たとえばfindAllAsync()などにより非同期クエリが実行された場合変更内容を計算する処理はバックグラウンドスレッドで実行され、その後呼び出したスレッドで通知が行われます。DiffUtilを使った場合のような余分なメモリのオーバーヘッドはありません。

コレクションスナップショット

Realmの重要なデザインコンセプトの一つに、オブジェクトやコレクションがライブであるというものがあります。これは、たとえば検索結果を保持するRealmResultsは、データベース内のデータが変更されるとそれが自動的に反映されるということです。これはリアクティブアーキテクチャにとってとても効果的で、RealmChangeListenerを登録するだけで検索結果に影響する変更に対する通知を受け取ることができます。

しかし、このライブであるという特性が問題を引き起こす場合もあります。たとえばライブなコレクションに対してループで順番に要素にアクセスする場合です。次にそのような場合のコード例を示します。

RealmResults<Guest> uninvitedGuests = realm.where(Guests.class)
  .equalTo("inviteSent", false)
  .findAll(); 

for (i = 0; i < uninvitedGuests.size(); i++) {
  realm.beginTransaction()
  uninvitedGuests.get(i).sendInvite();
  realm.commitTransaction();
}

ここでは、招待されていない全てのゲストに対して招待を行うため未招待のゲストを取得しています。ここでuninvitedGuestsが通常のArrayListであれば、期待通り動作し全てのゲストに招待が行われます。

ところが、RealmResultsの場合は変更が自動的に反映されるため、ループの中でトランザクションを実行するとループが回る度にuninvitedGuestsに変更が反映されてしまいます。これにより招待が行われたゲストはリストから取り除かれ、要素が1つずつずれます。今回のコードでfor文はルーブのたびにインデックスを増やしているため、意図しない要素へアクセスすることになってしまします。結果として、全体の半分の人にしか招待が行われず、あきらかに意図した動作とは違います。

これに対しては2つの解決策が存在します。

// 1. ループ自体を一つのトランザクションの中で実行する
realm.beginTransaction()
for (i = 0; i < uninvitedGuests.size(); i++) {
  uninvitedGuests.get(i).sendInvite();
}
realm.commitTransaction();

// 2. リストに対して後ろから逆順にアクセスする
for (int i = uninvitedGuests.size() - 1; i >= 0; i--) {
  realm.beginTransaction()
  uninvitedGuests.get(i).sendInvite();
  realm.commitTransaction();
}

これらの方法はわかりやすいものではないため、Realm Java 0.89のリリースの際にRealmResultsに対するすべての更新を次のイベントループまで遅延させることを決めました(Handler.postAtFrontOfQueueを用いて実装されています)。

これはシンプルなforループが意図通りに動作するという利点をもたらしましたが、RealmResultが最新ではない状態が発生し、たとえばオブジェクトを削除した場合にもRealmResult中に無効なオブジェクトとして次のイベントループまで残り続けてしまうという欠点ももたらしました。

realm.beginTransaction();
guests.get(0).deleteFromRealm(); // Delete the object from Realm
realm.commitTransaction();
guests.get(0).isValid() == false; // You could now get a reference to a deleted object.

3.0においてこの判断について再度検討を行い、RealmResultsを以前のように完全にライブなものに戻すことを決めました。ただし、コレクションに対して通常通りforループを使えるようにするため、createSnapshot()メソッドを追加しています。このメソッドが返すOrderedRealmCollectionSnapshotはこれまで通りの変更の影響を受けないコレクションとして使用することができます。for文の中で要素を変更する場合は、このメソッドを用いて作成したスナップショットを使用してください(ただしループの中でトランザクションを作成することはほとんどの場合においてアンチパターンであることに変わりはありません)。

RealmResults<Person> uninvitedGuests = realm.where(Person.class).equalTo("inviteSent", false).findAll();
OrderedRealmCollectionSnapshot<Person> uninvitedGuestsSnapshot = uninvitedGuests.createSnapshot();
for (int i = 0; uninvitedGuestsSnapshot.size(); i++) {
    realm.beginTransaction();
    uninvitedGuestsSnapshot.get(i).setInvited(true);
    realm.commitTransaction();
}

イテレーターはこのスナップショットを内部で使用しているためfor-eachは意図通りに動作します。

realm.beginTransaction();
RealmResults<Person> uninvitedGuests = realm.where(Person.class).equalTo("inviteSent", false).findAll();
for (Person guest : guests) {
    realm.beginTransaction();
    uninvitedGuests.setInvited(true); 
    realm.commitTransaction();
}

この変更を行った理由はいくつかありますが、そのうちの一つはコレクションの詳細な変更通知を実装するにあたって内部のリファクタリングが必要だったということがあります。他には、RealmResultsRealmListの両方が完全にライブなものになるため、Realmの全てのクラスおいて完全に同じセマンティクスを提供できるというものがあります。これにより、ドキュメントも理解しやすいものにできます。以前の動作は混乱を招く場合もありましたが、完全にライブであることとそれに対するドキュメントの改善により、今まで以上に強力なAPIを提供できると考えています。

今回の変更が既存のコードベースに影響する場合があることは理解してますが、その影響はforループの中でトランザクションを作成している場合に限られるはずです。それ以外のコードについてはこれまで通り動作するので、長い目で見ればよい変更であったと思っていただけることを期待します。

関連プロパティを用いたソート

これまで、RealmResultsの要素のソートは対象クラスが直接持つプロパティでのみ行うことができました。Realm Java 3.0では、対象クラスが関連 関連として持っているクラスのプロパティを用いてソートできるようになりました。

たとえば以下のように、Personのコレクションをそれぞれが関連として持っているdogオブジェクトの年齢プロパティによってソートできるようになります。

RealmResults<Person> persons = realm.where(Person.class).findAllSorted(dog.age, Sort.ASCENDING);

これら以外についても、直近のリリースでさまざまなバグフィックスを行っています。変更の完全なリストについては、changelogをご確認ください。


お読みいただきありがとうございます。 Realm で素晴らしいアプリケーションを作りましょう!お困りの際はStack Overflow(日本語)Slack(日本語)Twitter(日本語)GitHub(英語)でご相談ください。

Read more

Realm + Microsoft: Xamarin, Azure, and Windows Desktop

by /

Realmのミッションは優れたアプリケーションを簡単に開発できるようにすることです。できるだけ多くのデベロッパー支えたいと考えています。本日はマイクロソフト製品のデベロッパーの方々が、Realmを使い高い可用性を持ち、リアルタイムなアプリケーションを簡単に開発できる環境をご用意しました。Xamarin版のRealm Mobile DatabaseがRealm Mobile Platformをサポートし、同期などサーバーサイドとの連携機能が利用できるようになりました。また、Realm Mobile Databaseは新しくWindowsデスクトップ環境をサポートしました。

RealmはマイクロソフトのXamarinチームと協力し、XamarinとAzureを使ったサンプルアプリとチュートリアルを作成しました。新しい機能がどれだけ強力であるかをご覧いただけます。ぜひ記事を読み、Realmとマイクロソフト製品がどのように協調するのかをご覧ください。

XamarinとRealm: Xamarin版Realm Mobile Platformと、Realm Mobile Databaseの新機能

この間、私たちは完全に無料でオープンソースのオブジェクトデータベースであるRealm Xamarinを改善し続けていました。数か月前にはプロパティ(オブジェクトレベル)の通知、逆方向の関連のサポートとテスト、さらなるテストを繰り返し、この度Realm Xamarinはベータ版を脱し、1.0としてリリースしました。これまで継続的に支えてくださったデベロッパーコミュニティにお礼を申し上げます。この間に、521もの問題が報告され、解決できました。

しかし、それはまだすべてではありません。 本日よりRealm Mobile Platformは、Xamarinをサポートしています。 つまりC#デベロッパーはアプリケーションをRealm Object Serverに接続するだけで、クロスプラットフォームのリアクティブアプリケーションを構築できます。 Realm Mobile Platformは、コンフリクトの自動解消、ユーザー認証、およびカスタマイズ可能なアクセスコントロールなど、リッチでリアルタイムなユーザー体験を構築するためのすべてを提供します。シリアライゼーションやネットワーキングのコードを記述する必要はありません。また、データはクライアント側のRealm Mobile Databaseに保存されているため、ユーザーはオフラインであってもファーストクラスの体験を得られます。

このような機能がアプリにどのように役立つのでしょうか?下記のビデオをご覧ください。

このお絵描きアプリケーションは誰でも簡単に作れます。先ほどのマイクロソフトのXamarinチームと協力して作成したチュートリアルをご覧ください。このチュートリアルでは、リアルタイム機能を持つクロスプラットフォームのアプリケーションをXamarinを用いて構築し、Azure上にデプロイしたRealm Object Serverを用いてデータを同期します。3月9日に行われるウェブセミナーではさらに詳しく解説します。ぜひご参加ください

すでにRealm Mobile Platformをよくご存知の場合は、Realm Object Serverのドキュメントご覧になり、数行のコードを追加するだけで同期を利用できます。

Realm DatabaseがWindowsデスクトップをサポート

Realm Mobile Databaseは新しくWindowsデスクトップをサポートしました。Win32 APIをサポートする数億台のコンピュータでRealmが利用できます。Realmを用いてモバイルアプリと同様の機能を持ったデスクトップアプリケーションを作成できます。シンプルにデータモデルを定義し、オブジェクトを作成するだけです。そしてトランザクションを開始してオブジェクトをディスクに保存します。RealmはACIDに準拠しています。アプリケーションを開発する際にはドキュメントを参照してください。

Realmとマイクロソフト製品で開発を始めましょう

超巨大なデベロッパーコミュニティに新しいツールを提供できることを非常に嬉しく思っています。Realm、Xamarin、マイクロソフト製品で作られたアプリが出てくるのを待ちきれません。Realm Xamarinのドキュメントをご覧ください。新しい機能を見たい方は、3/9のテクニカルウェブセミナーに参加してください。

Read more

Realm React Native 1.0: Realm Mobile Platformに対応しました。

by /

本日はReact Nativeコミュニティのみなさまに2つのお知らせをいたします。

1つめのお知らせは、オープンソース化から約1年を経てRealm React Nativeがバージョン1.0となったことです。これは強力なオブジェクトデータベースとしての、そして素晴らしいリアクティブアプリのための基礎としての大きなマイルストーンに到達したことを意味します。そしてもう一つのお知らせは、Realm Mobile Platformのサポート対象にReact Nativeが新たに追加されたということです。Realm React Nativeを利用していただいている開発者の方々には、数行のコードの追加で、新たな機能を利用できるようになります。

昨年Realm React Nativeをベータ版としてリリースしてから今日まで、React NativeのコミュニティやTaskRabbitなどの企業にご利用いてきました。Realmはさまざまなプラットフォームで利用可能なデータベースであるため、コードの優れた再利用性と堅牢性を提供します。また、Realmのライブオブジェクトとコレクション通知は、Realm上のデータ更新に対して真にリアクティブなアプリケーションの開発を可能にします。Realmを使い始めるのはとても簡単です。詳しくはRealm React Nativeのドキュメントを参照してください。

Realmは単なるクライアントサイドで利用可能なデータベースにとどまりません。Realm React Native 1.0のリリースにともない、Realm Mobile Platformの機能を利用して強力なアプリケーションを開発可能になります。Realm Object Serverをつかってデータ同期可能なRealmを作成することで、プロパティの変更は自動的に同期されます。またこれらのデータはオフラインの際も利用できるようローカルにも保持されます。Realm Mobile Platformは単なるデータ同期だけでなく、コンフリクト解決、ユーザー認証、カスタマイズ可能なパーミッションなど複数デバイスにまたがった機能をもつアプリケーションに必要なすべての機能を提供します。

Realm Mobile Platoformのプロフェッショナル版およびエンタープライズ版では、サーバーサイドでもJavaScriptによるリアクティブなコードの記述を可能にするEvent Handling機能が提供されています。これによりコードベース全体でリアクティブ原則を取り入れることが可能になり、いつどこで発生したデータ変更に対しても反応することができます。

既存のReact NativeアプリケーションをRealm Mobile Platoformに統合するのはとても簡単です。ユーザーの認証後Realm Object Serverに接続し、アプリケーションのUIをリアクティブに更新するサンプルコードは以下のようになります。


import React, { Component } from 'react';
import { Text } from 'react-native';
import Realm from 'realm';
import { ListView } from 'realm/react-native';

export default class DogsView extends Component {
    constructor(props) {
        super(props);
        // Initialize the component with an empty data source
        const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
        this.state = { dataSource: ds };
    }

    componentWillMount() {
        // `this.props`はDogsViewでつかうusernameとpasswordを保持しています。
        Realm.Sync.User.login('https://my-realm-server.com', this.props.username, this.props.password, (error, user) => {
            let realm = new Realm({
                schema: [ { name: 'Dog', properties: { name: 'string' } } ],
                sync: { user, url: 'realms://my-realm-server.com/~/dogs'}
            });
            // ログインが完了後、同期を有効化したRealmインスタンスを作成します。
            // Realm内の全Dogオブジェクトを保持するコレクションを取得し、DataSourceを初期化するとともに
            // コレクションにセットしたリスナーでコレクション要素の更新をUIに反映します。
			const dogs = realm.objects('Dog');
            this.setState({ realm, dataSource: this.state.dataSource.cloneWithRows(dogs) });
			dogs.addListener(() => this.forceUpdate());
        });
    }

    render() {
        return (<ListView dataSource={this.state.dataSource} renderRow={(item) => <Text>{item.name}</Text>} />);
    }
}

Realm Mobile Platformを使い始めるには公式ドキュメントを参照してください。オンライン、オフラインに関わらず動作するユーザーに愛されるアプリケーションの開発を始めましょう。

Read more