ページ

2009年11月17日火曜日

BlogAssistant(1) - CoreData を NSTableView へ表示させる(NSArrayController経由でバインド)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

以前、書いたようにブログ書き支援用の小アプリを作る。
Cocoaの日々: SimpleCapへ Webページのスクリーンショット機能を追加する(予定)

HTMLのAタグとサムネイル画像を作るようなアプリを考えている。名前は BlogAssistant にしよう。このデータを CoreData で管理させようと思っていろいろ試行錯誤している。今回は簡単なモデルを作成し、それを NSTableView へ表示させてみよう。

Xcodeで Core Data Application を作成する

(以下、MacOSX10.5、XCode 3.1.1で操作)

新規プロジェクトを作る。後々 Safariのプラグインに変える予定だが CoreDataの挙動を確認しながら作りたいのでまずは単体のアプリケーションとして作る。Xcodeで新規プロジェクトを作成しテンプレートの中から Core Data Application を選択する。



自動的に *_AppDelegate クラスが生成される。


このクラスにはあらかじめ Core Data 永続化スタックを使うのに必要なコードが自動的に用意されている。ヘッダファイルはこんな感じ。
BlogAssistant_AppDelegate.h

#import

@interface BlogAssistant_AppDelegate : NSObject
{
    IBOutlet NSWindow *window;
  
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
- (NSManagedObjectModel *)managedObjectModel;
- (NSManagedObjectContext *)managedObjectContext;

- (IBAction)saveAction:sender;

@end

これをベースに作って行こう。


モデルの定義

*.xcdatamodel を開いてデータモデルを定義する。エンティティ Homepage をこんな感じで作ってみた。

併せてモデルクラスを用意する。

新規ファイルを作成し、属性をプロパティ定義していく。このあたりXCodeが自動生成してくれると良いのだが(何か方法があるのだろうか?)。
Homepage.h

#import

@interface Homepage : NSManagedObject {

}
@property (retain, nonatomic) NSDate * createdDate;
@property (retain, nonatomic) NSString * imageName;
@property (retain, nonatomic) NSString * memo;
@property (retain, nonatomic) NSDate * modifiedDate;
@property (retain, nonatomic) NSString * title;
@property (retain, nonatomic) NSString * url;

@end



Homepage.m

#import "Homepage.h"

@implementation Homepage

@dynamic createdDate;
@dynamic imageName;
@dynamic memo;
@dynamic modifiedDate;
@dynamic title;
@dynamic url;

@end



@dynamic 指定している件については以前のブログを参照されたし。
Cocoaの日々: CoreData - NSManagedObject のプロパティ


テーブルビューの用意とバインディング

InterfaceBuilder を立ち上げ、ウィンドウへテーブルビューを配置する。


続いてモデルとビューの橋渡しを行う NSArrayController を MainMenu.xib へ追加する。


これを MainMenu.xib へ追加。

インスペクタを開き、Attributes を編集する。

変更点は下記の通り:
  • Mode を Entity へ変更する
  • Entity Name へ先ほど用意したモデルでのエンティティ名 Homepage を指定する
  • Prepares Content にチェックを入れる(これで NSArrayController が自動的に NSManagedObjectContext 経由で保存データを読み込んでくれる)。 

次にテーブルビューのカラムを選択した後、NSArrayController へバインドする。

Controller Key へ arrangedObjects を、Model Key Path に title(カラムに表示するモデルの属性)を指定する。

他のカラムも同様にバインドしておく。

ここまででコントローラー(NSArrayController)とビュー(NSTableColumn)とのバインドができた。後はコントローラとモデルの紐付けが必要。NSArrayController は(親クラスの NSControllerは)CoreDataをサポートしていて -[setManagedObjectContext:] がある。これを使ってコントローラとモデルを結びつける。
参照:Mac Dev Center: NSObjectController Class Reference

BlogAssistant_AppDelegate.h

@interface BlogAssistant_AppDelegate : NSObject
{
      :
    IBOutlet NSArrayController *arrayController;
}



ヘッダへアウトレットを一つ追加し、InterfaceBuilderでNSArrayController へ接続する。

最後に NSArrayController へ NSManagedObjectContext を設定する。これは今回コードで書く。

BlogAssistant_AppDelegate.m

- (void)awakeFromNib
{
[arrayController setManagedObjectContext:[self managedObjectContext]];

}



これで Model-View-Controller が全てつながった。データの管理は NSManagedObjectContext が良きに?取りはからってくれる。


テスト用ボタン追加

表示を確認するにはテストデータが必要。テストデータを追加するボタンを付ける。

ボタンのターゲットは BlogAssistant_AppDelegate とし、アクションは addTestRecord: につなぐ。実装はこんな感じ。

BlogAssistant_AppDelegate.m

- (IBAction)addTestRecord:(id)sender
{
Homepage* homepage = [NSEntityDescription insertNewObjectForEntityForName:@"Homepage"
inManagedObjectContext:[self managedObjectContext]];
homepage.title = [NSString stringWithFormat:@"TEST TITLE:%@", [[NSDate date] description]];
homepage.imageName = @"TEST IMAGE";
homepage.url = @"http://xcatsan.com/";
homepage.createdDate = [NSDate date];
homepage.modifiedDate = [NSDate date];
homepage.memo = @"MEMO";

NSError* error = nil;
[[self managedObjectContext] save:&error];

if (error) {
NSLog(@"INSERT ERROR: %@", error);
} else {
NSLog(@"INSERTED");
}

}


保存形式を SQLite へ変更する

自動生成されたコードでは保存形式が XMLになっている。これを SQLへ変更する。


BlogAssistant_AppDelegate.m

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {

    url = [NSURL fileURLWithPath: [applicationSupportFolder stringByAppendingPathComponent: @"BlogAssistant.db"]];
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]){
        [[NSApplication sharedApplication] presentError:error];
    }  


}



動作確認

動かしてみよう。起動して何度かボタンを押してテストデータを生成する。


NSManagedObjectContext へ対するテストデータ追加がテーブルビューへ自動的に反映された。ダブルクリックすると変更もできる。

アプリを止めて再起動するとデータが復元される。

ここまでで MVCの連携コードを書いて InterfaceBuilder を設定しただけ。CoreDataとBindingsを使うと、ローカルDB付きのテーブルビューがこんなに簡単にできてしまう。


保存データ

さてデータはいったいどこに保存されているのか。自動生成されたコードを見るとライブラリ配下の Application Support にあるようだ。

    url = [NSURL fileURLWithPath: [applicationSupportFolder stringByAppendingPathComponent: @"BlogAssistant.db"]];



ターミナルを開き sqlite3 コマンドで中身が確認できる。




ソースコード

github へ上げました。
BlogAssistant at master from xcatsan's SampleCode - GitHub



おまけ:SQLite のログ

実行時オプションにを -com.apple.CoreData.SQLDebug 1 付けるとデバッガコンソールへ SQLite のログを出力させることができる。

(参考)Cocoa Touch の日々: CoreData で発行されている SQL をデバッグ出力する

こんな感じ。


- - - -
BlogAssistant開発:続く..