はじめに
「RubyMotion で CoreData を使うなら MotionData を導入した方がいい」って勧めておいてなんだけど、 その後『MotionDataWrapper』を知って速攻で移行してしまった。
MotionDataWrapper の特徴
MotionData は DataMapper ライクに CoreData のエンティティを定義したのに対し、 MotionDataWrapper では Xcode Data Modeler で作成したエンティティ定義を使うことが前提になっている。
Xcode Data Modeler で作成したエンティティ定義を使うと割り切っているため、 MotionDataWrapper の実装はコンパクト。 CoreData の初期化と ActiveRecord ライクな Finder が機能のほとんどを占める。 非常に見通しがいいので、すべてのコードを読むのは容易だった。
Xcode Data Modeler を使うので、スキーマのバージョ二ングができる。 試してないけど、マイグレーションもできそう。 これらは MotionData にはまだ無い機能だ。
MotionDataWrapper のインストール
Gemfile に
gem 'motion_data_wrapper'
を記述して、
bundle
を実行すればインストールできる。
MotionDataWrapper を使ったサンプル
上記の記事で作成したサンプルを、MotionDataWrapper を使って書き変えてみる。
エンティティの定義
エンティティの定義は Xcode を使う。
まず Xcode で空のプロジェクトを新規作成。
xcodeproj ファイルの保存先は 、app ディレクトリと resources ディレクトリ以外ならどこでもいい。
次に Data Model ファイルを作成。
保存場所は resources ディレクトリを選択する。
スキーマのバージョンを指定して
エンティティを定義したら終了。
MotionDataWrapper を使ってコードを記述
Xcode Data Modeler で定義したエンティティに対応するモデルクラスを、MotionDataWrapper::Model を継承して作成する。
class Entry < MotionDataWrapper::Model end
AppDelegate で MotionDataWrapper::Delegate をインクルードして、CoreData 関連オブジェクトを(ManagedObjectContext など)を取得するメソッドを追加。
class AppDelegate # CoreData の初期化は Delegate が中でやってくれる include MotionDataWrapper::Delegate # ...省略... end
データの CRUD 操作に関しては、MotionDataWrapper::Model が ActiveRecord ライクなメソッドを提供してくれる。
MotionDataWrapper を使ったサンプルコードの全体は次の通り。
# coding: utf-8 class AppDelegate # CoreData の初期化は Delegate が中でやってくれる include MotionDataWrapper::Delegate def application(application, didFinishLaunchingWithOptions:launchOptions) nav = UINavigationController.alloc.initWithRootViewController( MasterController.alloc.init ) @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) @window.rootViewController = nav @window.makeKeyAndVisible true end end # モデルの定義はこれだけ class Entry < MotionDataWrapper::Model end class MasterController < UITableViewController def viewDidLoad super navigationItem.title = 'Guestbook' navigationItem.leftBarButtonItem = editButtonItem navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem( UIBarButtonSystemItemAdd, target:self, action:'addEntry' ) end def addEntry # 追加 Entry.create(creation_date: NSDate.date) @entries = nil view.reloadData end # SQLite に保存したデータを取得。 def entries @entries ||= Entry.all end def tableView(tableView, numberOfRowsInSection:section) entries.size end CellID = 'CellIdentifier' def tableView(tableView, cellForRowAtIndexPath:indexPath) cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CellID) entry = entries[indexPath.row] @date_formatter ||= NSDateFormatter.alloc.init.tap do |df| df.timeStyle = NSDateFormatterMediumStyle df.dateStyle = NSDateFormatterMediumStyle end cell.textLabel.text = @date_formatter.stringFromDate(entry.creation_date) cell end def tableView(tableView, editingStyleForRowAtIndexPath:indexPath) UITableViewCellEditingStyleDelete end def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath) # 削除 entry = entries[indexPath.row] entry.destroy @entries = nil tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimationFade) end end
scope を定義できないのが不満だけど、Pull Request を覗いてみたら実装中だったので、 そのうちマージされそう。
まとめ
- Xcode Data Modeler で作成したエンティティ定義(Data Model ファイル)を使う
- ActiveRecord ライクなメソッドが使える
- scope はまだない
作り捨てのアプリなら MotionData でもいい。 長く保守するつもりのアプリなら、スキーマの変更がいつか必要になるから、現時点では MotionDataWrapper を使った方がいいと思う。