アプリケーションに Core Data のデータベースを追加する

ここでは、普通のウィンドウベースのアプリケーションに Core Data のデータベースを追加する方法をメモする。

基本の new xcode project で作った Window があるだけのアプリケーションに、データベースを追加してみる。せっかくなのでアプリケーションにしてしまえ、ということで、漢字と読みのデータベースで漢字を読みに変換するアプリケーションにしてみる。適当に作るので実用性はないと思う。

まずは、Core Data Framework を使えるようにしないといけないので、AppController.rb ファイルに OSX.require_framework 'CoreData' を追加する。

require 'osx/cocoa'

OSX.require_framework 'CoreData'

つぎに MainMenu.nib をダブルクリックして Interface Builder で開く。Info で、Development Target を Mac OS X 10.5.x にする。

したら、Library から Window を選び、nib ファイルに追加する。

Inspector の Attribute で Title を入力する。これは、ウィンドウのタイトルバーに現れる文字列になる。このウィンドウはアプリケーションから呼び出してデータベースのデータを変更するようにしたいので、起動時には表示しないために、Visible At Launch のチェックを外す。それと、ウィンドウを閉じても、内容を保持してまた開いて使えるように Release When Closed のチェックも外しておく。もひとつ、これはデータ入力のためのウィンドウなのでリサイズされたくないので Resize のチェックも外す。

次に、Size でこのウィンドウが表示される位置を決める。Initial Position でウィンドウを動かして左上の方にする。

ここで、Xcode に戻って、アウトレットを追加する。ここでは、databaseWindow としておく。

ib_outlets :window, :databaseWindow

また Interface Builder に戻り、このアウトレットと Database ウィンドウを結びつける。

次に、データベースを追加するために、データモデルをプロジェクトに追加する。

Xcode に戻って、グループとファイルのプロジェクトを右クリックして新規グループを選ぶ。

これに Models という名前をつけておく。

こんどは、この Models を右クリックして、新規ファイルを選ぶ。

Cocoa のデータモデルを選んで次へをクリックする。名前は何でもいいけど、Core Data アプリケーションで (project name)_DataModel となっていたので、それにあわせてここでは、Sample_DataModel とする。最後のところは無視して完了をクリックして終わる。

これで、DataModel が追加されるので、ダブルクリックして開く。

ここの細かい作業は、CoreData アプリケーションを作るを参照。ここでは、Yomikata というエンティティを作り、kanji と yomi という属性を追加する。両方とも文字列にする。

これを Database ウィンドウにドラッグアンドドロップして配置するとこんな感じ。

ここで一工夫。Core Data アプリケーションとして作ると AppDelegate が作られるんだけど、このアプリケーションにはないので、Array Controller をバインドする先は App Controller になる。ただ、そこには Core Data データベースを扱うコマンドはないので、自分で用意することになる。今のところ細かいことがわかっていないので、荒技として、Core Data アプリケーションの AppDelegate ファイルから持ってくることにする。CoreData アプリケーションを作るで作ったサンプルから、クラスのメソッドを全部コピーして持ってくる。class AppDelegate < OSX::NSObject から最後の end までの間全部。

ただし、いくつか変更するところがある。

まずはデータベースの xml ファイルが保存されるフォルダの指定。前のサンプルでは CoreData_test なので、これを今作っているアプリケーション名に変える。スペースはそのままでもいいと思うが、勝手に作られるときはアンダーバーに置き換えられていたので、ここでもそうしておく。

変更後はこんな感じ。

次に、xml ファイルのファイル名を変える。

これも、別に何でもいいんだけど、元のがアプリケーション名になっていたので、ここではそれに従う。

Core Data アプリケーションを作るにも書いたように、ここで、ファイル名の4行後にある NSXMLStoreTypeNSSQLiteStoreType に変えると、SQLite データベースファイルにデータを保存するようにできる。

あと、最後に Interface Builder に戻って、nib ファイル上の Application の Delegate に App Controller を結びつける。

これで、Xcode に戻ってビルドと実行をすると問題なく立ち上がるはず。ただ、Database ウィンドウを開くメソッドがないので、開けない。それをする前に、Database のインターフェイスをせっかくだからちょっといじってみる。

Interface Builder に戻って、Database ウィンドウのテーブルビューを選んで、Inspector の Attribute で Highlight を Source List にして、Alternate Rows にチェックを入れる。これで、テーブルが一行おきに色付けされ、ハイライトの色が変わる。

次に、ボタンを Rounded Rect Button にしてみる。Library から選ぶ。

3つとも換えてみた。こんな感じ。

次に新しいボタンを Array Controller にバインドする。ボタンを選んで Inspector で Binding を選ぶ。まずは、Add ボタン。Availability の Enabled を開いて、バインド先を Yomikata Array Controller(インターフェイスを追加したときに作られたもの)にして、Bind to にチェックを入れる。Controller Key には canAdd を選ぶ。Remove も同様にバインドして、Controller Key には canRemove を選ぶ。Fetch はバインドしなくていい。というか、Fetch はデータを受信、とか書いてあるけど、ローカルでアプリケーション上でどう作用するのかわからない。わかったらまた情報を追加していく。ローカルなデータベース一つならボタンなくてもいいんじゃないかと思うんだけど。

バインドし終わったら、今度は動作を設定していく。Array Controller を選んで、右クリックするか Inspector の Connections を開いて、Received Actions の add: を Add ボタンに、fetch: を Fetch ボタンに remove: を Remove ボタンに結びつける。

最終的には、こんな形にしてみた。

これで、データベース部分は動くはずなんだけど、アプリケーションとしては何もしてないので、このウィンドウさえ現れない。ということで、メインのウィンドウをいじってみる。

Wrapping Text Filed とボタンを2つ配置した。一つは Database ウィンドウを開くもの、もう一つは漢字を読みに変換するもの。

まずは、Xcode に戻って、この3つのパーツのアウトレットを作り Interface Builder で結びつける。

Xcode に戻り、Database ウィンドウを開くアクションを追加する。ただ単に、開くウィンドウのアウトレットに makeKeyAndOrderFront() というメソッドを使うだけ。

Interface Builder でこのアクションを Database ボタンに結びつける。

これで、Xcode に戻ってビルドと実行をすると、Database ボタンをクリックしたときに Database ウィンドウが開いて、データベースの操作ができるようになる。データは、アプリケーションを終了するときにファイルの保存される。

つぎに、Yomi ボタンを押したときの動作を作る。ここでは、入力されたテキストの漢字を置き換えたい。ただ、厳密に始めると形態素分析をして分かち書きをしてからでないと、というように日本語では泥沼にはまるので、ここでは、単語ごとにスペースで区切って分かち書きのように入力していき、その単語ごとに登録してある漢字と一致するか判断して、データベースにあれば置き換える、というものにする。同じ漢字で複数登録してあっても、最初に見つかったもので置き換える。yomiHenkan というメソッドを作り、これを ib_action で Interface Builder の Yomi ボタンと結びつける。

スクリプトは、こんな感じ。

最初に、inputText に Ruby String オブジェクトとして文字列を読み込み、全角半角のスペースで分割して(split)配列に入れて、重複するものは uniq で取り除く。この配列に入っている単語ごとにブロックで置き換えの処理をしていく。

この処理では、まず、データを受け取るために request という NSFetchRequest オブジェクトを作る。次に entity という NSEntityDescription オブジェクトを entityForName_inManagedObjectContext というメソッドを使って、エンティティの名前を指定して作る。ManagedObjectContext は、Core Data アプリケーションを作ったときに @managedObjectContext で作られていて、そのスクリプトを拝借したのでそれを指定する。これを setEntity() メソッドで指定して、Yomikata からデータを受け取るようにする。

つぎに、NSPredicate オブジェクトを predicateWithFormat というメソッドで作る。いわゆる SQL の SELECT で where を使うときの指定みたいな感じ。'kanji like %@' というように like を使って kanji が その後に指定する値である列のデータを受け取る。ここでは、word の値が使われる(%@ の部分)。これを setPredicate(predicate) メソッド指定する。

最後に executeFetchRequest_error(request,error) で送信する。これは NSManagedObjectContet クラスのメソッドなので、@managedObjectContext にたいして行う。返ってくるのは NSArray オブジェクトなので、to_a で Ruby Array オブジェクトに変換して、配列の最初の要素から、valueForKey('yomi') で読みを取り出す。これを使って、元のテキストの対応する漢字と置き換える。

ブロックの処理が終わったら、Text Field に setStringValue で漢字を読みに置き換えた文字列を表示する。

これで、一応想定したような動作をするはず。

データを追加するときは、insertNewObjectForEntityForName_inManagedObjectContext(entity,managedObjectContext) で NSEntityDescription オブジェクトを作り、要素を追加していく。(NSEntityDescription オブジェクト) に属性をくっつけて入れていく。yomikata というオブジェクトを作って、kanji という属性に要素を追加する場合は、

yomikata.kanji = "漢字"

とする。まとめて書くとこんな感じ。

yomikata = NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext("Yomikata",@managedObjectContext)

yomikata.kanji = "漢字"

yomikata.yomi = ”かんじ”

最後に、このアプリケーションはシングルウィンドウなので、ウィンドウをすべて閉じたときにアプリケーションを終了するようにしてみる。

AppController.rb に applicationShouldTerminateAfterLastWindowClosed(application) というメソッドを追加する。

Interface Builder で、nib ファイルの File's Owner の delegate を App Controller と結びつける。

これで、ウィンドウを閉じるとアプリケーションが終了する。