RubyMotion で MagicalRecord を使う

最近、RubyMotion 向けのラッパー gem じゃなくて、 Objective-C のライブラリを使ったほうがいいのでは、って思うようになってきた。 CoreData なら MotionData や MotionDataWrapper じゃなくて MagicalRecord。

そこで RubyMotion で MagicalRecord が使えるか試してみる。

まず CocoaPods で MagicalRecord を追加する。 motion-cocoapods を使っているから、Rakefile

Motion::Project::App.setup do |app|
  app.name = 'MagicalRecordSample'

  # CocoaPods で MagicalRecord をインストール
  app.pods do
    pod 'MagicalRecord'
  end
end

を記述して

bundle exec rake pod:install

を実行。

次に Xcode で Empty プロジェクトを作成する。

f:id:griefworker:20131106230212p:plain

プロジェクトに Data Modelファイルを追加。ファイルの保存先は resources 直下で、 ファイル名は Model のままにしておく。

f:id:griefworker:20131106230227p:plain

エンティティを定義したら Xcode での作業は終了。

f:id:griefworker:20131216194751p:plain

MagicalRecord を使って RubyMotion のサンプルを書いてみる。

# coding: utf-8

class Task < NSManagedObject
end

class TasksViewController < UITableViewController
  def viewDidLoad
    super
    self.navigationItem.title = "Tasks"
    self.navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(
      UIBarButtonSystemItemAdd,
      target: self,
      action: "add_task"
    )
    self.navigationItem.leftBarButtonItem = self.editButtonItem
  end

  def tasks
    @tasks ||= Task.MR_findAll
  end

  def add_task
    task = Task.MR_createEntity
    task.title = "test"
    task.createdAt = NSDate.date
    NSManagedObjectContext.MR_defaultContext.MR_saveToPersistentStoreAndWait

    @tasks = nil
    self.tableView.reloadData
  end

  def tableView(tableView, numberOfRowsInSection:section)
    self.tasks.size
  end

  def tableView(tableView, cellForRowAtIndexPath:indexPath)
    task = self.tasks[indexPath.row]
    cell = tableView.dequeueReusableCellWithIdentifier("Cell")
    if cell == nil
      cell = UITableViewCell.alloc.initWithStyle(
        UITableViewCellStyleDefault,
        reuseIdentifier:"Cell"
      )
    end
    cell.textLabel.text = task.title
    cell
  end

  def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath)
    if editingStyle == UITableViewCellEditingStyleDelete
      task = self.tasks[indexPath.row]
      delete_task(task)
      tableView.deleteRowsAtIndexPaths(
        [indexPath],
        withRowAnimation:UITableViewRowAnimationFade
      )
    end
  end

  def delete_task(task)
    task.MR_deleteEntity
    NSManagedObjectContext.MR_defaultContext.MR_saveToPersistentStoreAndWait
    @tasks = nil
  end
end

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    MagicalRecord.setupCoreDataStackWithStoreNamed("Sample.sqlite")
    
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    @window.rootViewController = UINavigationController.alloc.initWithRootViewController(
      TasksViewController.alloc.init
    )
    @window.makeKeyAndVisible
    true
  end

  def applicationWillTerminate(application)
    MagicalRecord.cleanUp
  end
end

MagicalRecord のメソッドを呼び出すだけで CoreData の初期化ができるから、コードがすっきりした。 MotionDataWrapper を使ったときと大差ない。

RubyMotion で CoreData ばっかり扱ってきた気がするけど、それも今回が最後になるかな。