ページ

2010年2月2日火曜日

Cocoaの日々: BlogAssistant(12) - Queueの導入: plistの保存

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

(前回)Cocoaの日々: BlogAssistant(11) - 複数のプロセスからCoreData を使う 〜 Queue(もどき)の導入

プラグイン側でクリップした情報を plist形式で書き出す。アプリとやり取りするためのフォルダを Images と同じレベルで用意した。

場所:~/Application Support/BlogAssistant/Queue

フォルダ名は PathManager に管理させ、他のクラスはこれを参照する(-[PathManager queuePath])。存在しない場合は自動的に作られる。


プラグイン側では新規にモデルクラス ResourceTransfer を用意した。

ResourceTransfer.h
@interface ResourceTransfer : NSObject {

 NSDate * createdDate;
 NSString * imageFilename;
 NSString * title;
 NSString * url;
 NSString * uuid;
}
@property (retain) NSDate * createdDate;
@property (retain) NSString * imageFilename;
@property (retain) NSString * title;
@property (retain) NSString * url;
@property (retain) NSString * uuid;

-(BOOL)save;

@end

このクラスで Queue へ保存する plistファイルを作成する。このクラスは Resourceクラスとほぼ同じプロパティを持つ。本来なら Resource クラスで一元化できれば良いのだが、NSManagedObject のサブクラスである Resource を使うには Core Data Stack 一式を用意する必要がある。単にデータホルダーとしての役割の為だけに Core Data を使うのは無駄なのでメンテナンスの手間はかかるが別クラスとした。ビューアアプリ側では後ほどこのクラスを使って plistの内容から ResourceTransfer を作成し、さらにそこから Resource インスタンスを生成し CoreDataへ保存する。

また結果的にプラグイン側では Core Data をまったく使わなくなった。これに伴ない関係クラスやフレームワークをビルド対象から外した。このことでプラグインがコンパクトになり不安定になる要因を減らせたのは良かった。


save メソッドはこんな感じ。

ResourceTransfer.m
-(BOOL)save
{
 NSMutableDictionary* outputDict = [NSMutableDictionary dictionary];
 unsigned int outCount, i;
 objc_property_t *properties =
  class_copyPropertyList([self class], &outCount);
 
 for(i = 0; i < outCount; i++) {
  objc_property_t property = properties[i];
  NSString *propertyName =
   [NSString stringWithUTF8String:property_getName(property)];
  [outputDict setObject:[self valueForKey:propertyName]
        forKey:propertyName];
 }
 free(properties);

 NSString* filename = [self.uuid stringByAppendingPathExtension:@"plist"];
 NSString* path = [[[PathManager sharedManager] queuePath] stringByAppendingPathComponent:filename];
 return [outputDict writeToFile:path atomically:YES];
}
プロパティを NSMutableDictionary へ詰めて、それを -[NSMutableDictionary writeToFile:atomically:] で plist形式で書き出す。プロパティの一覧取得にはランタイム関数の class_copyPropertyList()を使った。 (参考)Cocoaの日々: @property の一覧を取得する
なおサムネイル画像の書き出しや読み込みは専用のクラスを用意してそちらにまかせるようにした。

ImageManager.h
@interface ImageManager : NSObject {

}
+ (ImageManager*)sharedManager;
-(NSImage*)thumnailImageFromView:(NSView*)view;
- (BOOL)writeView:(NSView*)view withFilename:(NSString*)filename;
- (NSImage*)readImageWithFilename:(NSString*)filename;

@end

最後にプラグインのメイン処理。だいぶすっきりした。
PluginController.m
-(void)addPage:(id)sender
{
 // determine view to save
 WebView* web_view = [sender representedObject];
 WebFrameView* frame_view = [[web_view mainFrame] frameView];
 NSView* doc_view = [frame_view documentView];

 // setup output data
 ResourceTransfer* resTran = [[[ResourceTransfer alloc] init] autorelease];
 resTran.title = [web_view mainFrameTitle];
 resTran.url = [web_view mainFrameURL];

 // save image file
 BOOL r1, r2;
 r1 = [[ImageManager sharedManager] writeView:doc_view
     withFilename:resTran.imageFilename];

 // save data
 if (r1) {
  r2 = [resTran save];
          :


さて実行してみよう。ページを開いてクリップする。

サムネイル画像が保存され
plistができた。
中身を見る。
いいようだ。

後はこれをビューアアプリで読み取ればいい。


コードは GitHub からどうぞ。
xcatsan's BlogAssistant at 2010-02-02 - GitHub

- - - -
なお Queueに格納するデータ形式は Cocoaの KeyedArchiver も考えたが、plist(XML)の方がデバッグしやすいし可搬性が高いので採用した。ChromeやFireFoxのプラグインなどでも同じ形式が書き出せればSafari以外でも使えるようになる、なんて目論見もある。