見出し画像

[cocoa][swift]マイDocumentクラス

一月の勉強会でMVCについてディスカッションすることになった。そこで場が盛り上がるよう、ネタとして自分がよく採用するマイDocumentクラスに発表する。

アプリケーションの設計法としてMVCが話題となることが多いが、それは、MVCはデザイン・パターンが話題になる以前のもので、今のデザイン・パターンから見ると複数のパターンが組み合わさった大きな枠組みのものだというのも理由としてあるのかな?

早速本題に入る。Appleの文書で説明されている伝統的なMVCは以下のとおり。

これは、この論文でも説明されている。

そして、Cocoa版は以下となる。

ViewとModelは直接やりとりしない。間にControllerを挟んでいるのが特徴だ。このControllerだが、一種類でない。NSApplicationDelegateだったりNSDOcumentだったりNSControllerだったりNSViewControllerだったり、アプリケーション独自のクラスだったりする。

iOS開発を始めたときから独自のDocumentクラスを用意するようにしている。

@interface Document : NSObject
@property (strong, nonatomic) NSString              *version;
+ (Document *)sharedDocument;
- (void)load;
- (void)save;
@end
@interface Document ()
- (void)_clearDefaults;
- (void)_updateDefaults;
- (void)_loadDefaults;
- (NSString *)_modelDir;
- (NSString *)_modelPath;
@end
@implementation Document
@synthesize version = _version;
+ (Document *)sharedDocument;
{
   static Document *_sharedInstance = nil;
   static dispatch_once_t onceToken;
   
   dispatch_once(&onceToken, ^{
       _sharedInstance = [[Document alloc] init];
   });
   return _sharedInstance;
}
- (id)init
{
   self = [super init];
   if (self) {
       _version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
   }
   return self;
}
- (void)dealloc
{
   self.version = nil;
}
- (void)load
{
   [self _loadDefaults];
   
   NSString    *modelPath = [self _modelPath];
   if ((! modelPath) || (! [[NSFileManager defaultManager] fileExistsAtPath:modelPath])) {
       return;
   }
}
- (void)save
{
   [self _updateDefaults];
   
   NSFileManager   *fileManager = [NSFileManager defaultManager];
   
   NSString    *modelDir = [self _modelDir];
   if (![fileManager fileExistsAtPath:modelDir]) {
       NSError *error = nil;
       [fileManager createDirectoryAtPath:modelDir
              withIntermediateDirectories:YES
                               attributes:nil
                                    error:&error];
   }
   
   NSString    *modelPath = [self _modelPath];
   [NSKeyedArchiver archiveRootObject:self.indexArray toFile:modelPath];
}
- (void)_clearDefaults
{
   if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
       [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"version"];
   }
}
- (void)_updateDefaults
{
   NSString    *versionString = nil;
   if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
       versionString = [[NSUserDefaults standardUserDefaults] objectForKey:@"version"];
   }
   if ((versionString == nil) || ([versionString compare:self.version] != NSOrderedSame)) {
       [[NSUserDefaults standardUserDefaults] setObject:self.version forKey:@"version"];
       [[NSUserDefaults standardUserDefaults] synchronize];
   }
}
- (void)_loadDefaults
{
   NSString    *versionString = nil;
   if ([[NSUserDefaults standardUserDefaults] objectForKey:@"version"]) {
       versionString = [[NSUserDefaults standardUserDefaults] objectForKey:@"version"];
   }
   if ((versionString == nil) || ([versionString compare:self.version] != NSOrderedSame)) {
       /* バージョン不一致対応 */
   }
   else {
       /* 読み出し */
   }
}
- (NSString *)_modelDir
{
   NSArray    *paths;
   NSString   *path;
   paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
   if ([paths count] < 1) {
       return nil;
   }
   path = [paths objectAtIndex:0];
   
   path = [path stringByAppendingPathComponent:@".model"];
   return path;
}
- (NSString *)_modelPath
{
   NSString   *path;
   path = [[self _modelDir] stringByAppendingPathComponent:@"model.dat"];
   return path;
}
@end

macOSのDocument-Based. Applicationsについて説明する。

主要なクラスは、以下の3つ。

- NSDocument: データ管理。
- NSWindowController: ウィンドウ管理。
- NSDocumentController: ドキュメント管理。

ファイルとDocument、Modelの関係を図だ。

macOSのDocument-Based Applicationのクラスの関係を図示する。

【関連情報】
- Cocoa Advent Calendar 2018
Cocoa.swift 2019-01
Cocoa.swift
Cocoa勉強会 関東
Cocoa練習帳
Qiita

この記事が気に入ったらサポートをしてみませんか?