cocoaのモデルデータ関連技術のメモ

指先を怪我したのでバンドエイドを貼ったら、trackPadが反応してくれない。モチベーションが落ちる。

ローカルへの保存方法

cocoaアプリのファイルの保存形式では大別して以下の4つがある。

  保存形式 大量データ 複雑なデータ その他
手書き 気合い 気合い 気合い  
NSKeyedArchiver 非公開 r/wに時間がかかる 重複処理あり  
CoreData 非公開 一部だけ読み出せる 重複処理あり undo/redoサポート
plist 公開 r/wに時間がかかる 重複処理なし  

この表だけを見ると、お手軽さや機能を考えればCoreData一択。

model-viewの同期方法

Cocoaアプリのモデルとviewの同期方法には主に以下の4つがある。

  依存性 code or nib その他
手書き   コード  
cocoa binding iOSで使えない nib  
fetchResultController CoreData前提 コード coreDataがiCloudStorageと相性が悪い
KVOとaction   コード  

CoreDataを使う場合は、FetchResultControllerとKVOを使って書くのがよさげ。 CoreDataを使わない場合は、ButtonなどはKVOで書いて、TableViewはデリゲートを使った手書きになりそう。

データの保存と違って、手法の混在できるので、適材適所で行うのが良い。

デバイス間のドキュメントの同期方法

調べている途中で気がついたのですが、CoreData Storage 又はCoreData in iCloudと呼ばれる技術はDeprecatedになりました。 CoreDataを使って、iCloud経由でドキュメントを同期できる夢の技術だったのですが、致命的なバグでもあったのでしょう。

で、デバイス間のドキュメント同期技術は以下の2つです。

  1. iCloud document storage

file単位に基づく同期。DropBoxのApple版といった所。PowerPointやWordのようなタイプの書類ベースのアプリとは相性が良いかもしれない。

変更部分だけのマージが出来ないので、巨大なバイナルファイルを丸ごと入れ替えになる。

  1. CloudKit storage

クラウドにデータを置くことが主眼。グラウドにあるデータが主となる。JSからの呼び出しをサポートしているので、webアプリを作る場合は必須。

ネットに繋がっていないと使えない欠点がある。FileMakerやEverNoteのようなパーソナルデータベース型のアプリとは相性が良いかもしれない。

ローカルキャッシュをCoreDataに、ネットに繋がっている時にCloudKitで同期を行うと、上記の欠点を解消できる。

デバイス間の操作状態同期方法

同じユーザーアカウントの別デバイスに操作状態を引き継げる。

NSUserActivityを設定するらしい。使ったことないけど。

https://developer.apple.com/jp/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html

NSOrderedSetの感想

NSOrderedSetが使えそうで使えない。

CoreDataでは、NSOrderedSetをサポートしている。しかし、NSArrayControllerがOrdersSetに対応していない。

bindingでNSTablbeViewの中に順列を使うには、NSSet & NSSortOrderの組み合わせで表現する必要がある。

bindingでなくNSFetchResultControllerを使用しても良い。

CoreData中のNSOrderedSetはCoreDataの思想とあまり相性が良くないのかもしれない。

bindingでのサポートだけでなく、CoreDataInCloudでもサポートが無かった。

地雷を踏み抜かないように、SortOrderを使った昔ながらの手法を使用した方が良い。

MacOSX用アプリ”CardBook.app”をビルドする

ノートテーカー系のアプリを作成しようと思い、参考になりそうなソースコードを漁っていると良さげなアプリをまた見つけた。

CardBook.app http://www.paullynch.org/macosx/cardbook/ https://github.com/pauldlynch/CardBook

細かい実装を丁寧にこなしている印象なメモアプリ。

../../../_images/CardBookSS01.png

修正

Copy&PasteやDrag&Drop時に例外を吐いたの少しだけ修正しました。 原因はNSArchiverの書き出しが現在のMacOSXではサポートされていないため。対応として、NSArchiverをNSKeyedArchiverに、NSUnarchiverをNSKeyedUnarchiverに書き換える以外は何もしていない。

修正箇所はファイルMyDocument.mの以下の2つのメソッドだけ。

[MyDocument copyRows:toPasteboard:]

- (void)copyRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard {
   NSArray *array = [self cardsForRows:rows];
   // NSArchiverをNSKeyedArchiverに置き換え
   NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];// <-- ここ
   NSMutableAttributedString *aString = [[NSMutableAttributedString alloc] initWithString:@""];
   NSEnumerator *e = [array objectEnumerator];
   Card *card;
   while (card = [e nextObject]) {
       [aString appendAttributedString:[card card]];
   }
   [pboard declareTypes:[self pboardTypes] owner:self];
   [pboard setData:data forType:CardPboardType];
   [pboard setString:[aString string] forType:NSStringPboardType];
   [pboard setData:[aString RTFDFromRange:NSMakeRange(0, [aString length]) documentAttributes:nil] forType:NSRTFDPboardType];
   [pboard setData:[aString RTFFromRange:NSMakeRange(0, [aString length]) documentAttributes:nil] forType:NSRTFPboardType];
   [aString release];
}

[MyDocument addCardFromPboard:atRow:]

- (void)addCardFromPboard:(NSPasteboard *)pboard atRow:(NSInteger)row {
   Card *card;
   NSData *data;
   NSString *type;

   type = [pboard availableTypeFromArray:[self pboardTypes]];

   if ([type isEqual:CardPboardType]) {
       NSEnumerator *e;
       NSArray *cardArray;
       data = [pboard dataForType:CardPboardType];
       // NSUnarchiverをNSKeyedUnarchiverに置き換え
       cardArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];//<--ここ
       e = [cardArray objectEnumerator];
       while (card = [e nextObject])
           [self addCard:card atRow:row];
   }
   if ([type isEqual:NSRTFDPboardType]) {
       data = [pboard dataForType:NSRTFDPboardType];
       card = [Card card];
       [card setCardRTFD:data];
       [self addCard:card atRow:row];
   }
   if ([type isEqual:NSRTFPboardType]) {
       data = [pboard dataForType:NSRTFPboardType];
       card = [Card card];
       [card setCardRTF:data];
       [self addCard:card atRow:row];
   }
   if ([type isEqual:NSStringPboardType]) {
       NSString *pboardString = [pboard stringForType:NSStringPboardType];
       card = [Card card];
       [card setCardString:pboardString];
       [self addCard:card atRow:row];
   }
}

修正したソースコードはここ。( CardBook-master.zip )

ソースコードの特徴

作成された年代的に以下の技術は使用されていない。

  • cocoa binding
  • core data
  • restore state
  • プロパティ
  • ARC
  • NSViewController

素直に、コードで書かれている。window位置が再生されるのは、保存ファイルに位置を書き込んでいるから。 データの保存は、伝統的なNSKeyedArchiverを使用している。

印刷時にはCardsViewクラスを使ってA4の紙に複数のカードを纏めて印刷可能にしている。最近印刷を前提としていないアプリばかりなので、これは時代的なものなのかもしれない。

他にも、AppleScript用のリソースやローカライズリソースなども存在しており、丁寧に作られているように見える。

UIの特徴

NSTableViewとNSTextEditViewの組み合わせで特に特徴的なものはない。

しかし、Wiki文法やmarkDownを使うことなく、RTFDな修飾を直接使う方がフツーに便利かもと再考させられた。

カード間リンクはない。 “フォーカス”機能で一部のカードだけをリストアップする機能がある。

標準のメモアプリの使用感が一番近いかもしれない。

GNUstep用アプリ”MyWiki.app”をビルドする

ノートテーカー系のアプリを作成しようと思い、参考になりそうなソースコードを漁っていると良さげなアプリを見つけた。

MyWiki.app http://wiki.gnustep.org/index.php/MyWiki.app

Objective-Cで書かれた、サーバーなしのDesktop用のwikiアプリ。

../../../_images/MyWikiSS01.png

残念ながら、十年以上前のコードなのでそのままではビルドが出来ないので、手直ししてビルド。 修正したソースコードはここ。( MyWiki_test.zip )

カレンダーViewとページ作成日を利用した日誌ページの作成機能が面白い。作成日でまとめるために自動で日誌が出来上がる。

../../../_images/MyWikiSS03.png

コードを読んでいて面白いのは、NSTextViewの本文中にコラムを作る手法が良くできている。 NSTextViewにNSTextAttachmentを使って、アイコンと文字列を表示させている。

../../../_images/MyWikiSS02.png

自作ノートテーカーアプリには、文書中に”注意”や”意見”を入れたいと思っていたのでこの機能はパクりたい。

ユーザーとしての使い方は文字列をnoteタグで囲むだけでコラムができるのはお手軽で良い。

問題はDrag&Dropやコピペがうまくゆかない。NSTextAttachmentをコピーするときに、NSTextAttachmentの内容を書き出せば可能かもしれない。

PlantUMLのsalt言語

以前、オブジェクトグラフを書くのにdot言語を使用した( xibファイルの中のオブジェクト図 )。

この時に「テキストデータからGUI設計用の図を生成するDSLはないものか?」と思っていた。 ググると意外な場所で見つかった。

有名なPlantUMLの中のsalt言語がそれ。(http://plantuml.com/salt)

@startuml
salt
{+
.   |<b>CurrencyConverter|*
--- | ---          | ---
.|{
.   | Doller:  | "Input"    |*
.   | rate:    | "input"    |*
.   | Yen:     |  output    |*
.   | [Reset]  |  [Convert] |*
}|*
}
@enduml

テキストファイル、mainWindow.puに以上のように書いて、

$ java -jar path/to/plantuml.jar -p -tpng < mainWindow.pu > mainWindow.png

と実行すると、

../../../_images/mainWindow.png

こんな画像が得られる。

メニューバーなども作ったり、makefileなども作って可能性を探った。 資料はここ。( plantUML-salt.zip )

結論:webアプリケーション用に設計されたようで、私の用途には表現が弱すぎる。スクリーンショットか手書きの方が良さげ。