現在、RubyMotion の評価中。無料で試用できないのはつらいけど、30日間は返金に応じてくれるみたいなので、ポチってみた。
関心ごとの筆頭は、RubyMotion で CoreData が使えるかどうか。ネットで見つけた記事はどれも Web API 呼び出すやつばかりだったんで。
結論を先に書くと、RubyMotion でも CoreData は使える。 RubyMotionSamples というサンプル集に CoreData と CoreLocation を使ったサンプルがあった。
上記のサンプルは分かりやすかった。RubyMotionSamples は一通り目を通すべき。 でも実際に手を動かさないと身に付かないので、CoreData をベタ書きした、さらにシンプルなサンプルを書いてみた。
まず新しいプロジェクトを作成。
motion create CoreDataSample
Rakefile 内で、使用するフレームワークに CoreData を追加する。
# -*- coding: utf-8 -*- $:.unshift("/Library/RubyMotion/lib") require 'motion/project/template/ios' begin require 'bundler' Bundler.require rescue LoadError end Motion::Project::App.setup do |app| app.name = 'CoreDataSample' app.frameworks += ['CoreData'] end
あとはひたすらコードを書く。Xcode では GUI で作成できたエンティティの定義も、RubyMotion だとコードで書く。NSManagedObjectContext を初期化したり、データの取得・追加・削除のやり方は Objective-C と変わらない。
今回は app_delegate.rb にすべて書いた。
# coding: utf-8 class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) nav = UINavigationController.alloc.initWithRootViewController(MasterController.alloc.init) nav.wantsFullScreenLayout = true nav.toolbarHidden = true @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) @window.rootViewController = nav @window.makeKeyAndVisible true end end class Entry < NSManagedObject # CoreData のエンティティを定義。 # Xcode では GUI で定義できたけど、RubyMotion ではコードを書かないといけない。 def self.entity @entity ||= begin entity = NSEntityDescription.alloc.init entity.name = 'Entry' entity.managedObjectClassName = 'Entry' entity.properties = ['creation_date', NSDateAttributeType].each_slice(2).map do |name, type| property = NSAttributeDescription.alloc.init property.name = name property.attributeType = type property.optional = false property end entity end end end class MasterController < UITableViewController def viewDidLoad view.dataSource = self view.delegate = self end # CoreData 関連のクラスを初期化 def managed_object_context @context ||= begin model = NSManagedObjectModel.alloc.init model.entities = [Entry.entity] store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model) store_url = NSURL.fileURLWithPath(File.join(NSHomeDirectory(), 'Documents', 'Guestbook.sqlite')) error_ptr = Pointer.new(:object) unless store.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:store_url, options:nil, error:error_ptr) raise "Can't add persistent SQLite store: #{error_ptr[0].description}" end context = NSManagedObjectContext.alloc.init context.persistentStoreCoordinator = store context end end def save error_ptr = Pointer.new(:object) unless managed_object_context.save(error_ptr) raise "Error when saving the model: #{error_ptr[0].description}" end @entries = nil end def viewWillAppear(animated) navigationItem.title = 'Guestbook' navigationItem.leftBarButtonItem = editButtonItem navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemAdd, target:self, action:'addEntry') end def addEntry # 追加 entry = NSEntityDescription.insertNewObjectForEntityForName('Entry', inManagedObjectContext:managed_object_context) entry.creation_date = NSDate.date save view.reloadData end # SQLite に保存したデータを取得。 def entries @entries ||= begin request = NSFetchRequest.alloc.init request.entity = NSEntityDescription.entityForName('Entry', inManagedObjectContext:managed_object_context) request.sortDescriptors = [NSSortDescriptor.alloc.initWithKey('creation_date', ascending:false)] error_ptr = Pointer.new(:object) data = managed_object_context.executeFetchRequest(request, error:error_ptr) if data == nil raise "Error when fetching data: #{error_ptr[0].description}" end data end 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] # 削除 managed_object_context.deleteObject(entry) save tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimationFade) end end
rake でシミュレーターを起動すると CoreData を使ったサンプルアプリが動く。
RubyMotion でも CoreData は使えたけど、結構なコード量になった。Vim でこれだけのコードを書くのはシンドイ。