AppSandBoxとNSSavePanelの関係

AppSandBoxとNSOpen/NSSavePanelの関係について今ひとつはっきりしていなかったので、ごく簡単なXcodeProjectで確認してみた。

確認に使った環境等

  • Xcode Version 8.3.3 (8E3004b)
  • macOS Version 10.12.5 (16F73)
  • MacBook Pro 10.1および13.1
確認に使ったコード
- (IBAction)saveFile:(id)sender {

    NSString *logFile = @"A Test String to save LogFile.";
    NSError *anError = nil;

    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    [format setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]];
    [format setDateFormat:@"yyyyMMdd-HHmmss"];

    NSString *stringTime = [format stringFromDate:[NSDate date]];

    NSString *logFileName = [NSString stringWithFormat:@"Wing Go Client Log - %@",stringTime];

    NSSavePanel *savePanel = [NSSavePanel savePanel];
    [savePanel setAllowedFileTypes:[NSArray arrayWithObject:@"txt"]];
    [savePanel setAllowsOtherFileTypes:NO];
    [savePanel setNameFieldStringValue:logFileName];

    [savePanel setMessage:NSLocalizedString(@"Select the place to save Logfile",
                                            @"Select the place to save Logfile")];

    if ([savePanel runModal] == NSFileHandlingPanelOKButton) {

        NSURL *selectedURL = [savePanel URL];
        NSString *selectedFile = [selectedURL path];

        BOOL logSuccess= [logFile writeToFile:selectedFile
                                   atomically:YES
                                     encoding:NSUTF8StringEncoding
                                        error:&anError];

        if(!logSuccess) {

            //Don’t look at an NSError unless you konw that something failed!
            if (anError) {

                NSLog(@"Error creating file: %@",anError);
                anError = nil;
                return;
            }

        }
    }
}

以下の条件で確認、結果を併記している。

  1. AppSandBox設定なし

    • 書類(Documents)フォルダがデフォルトの保存先になっている。
    • ファイルの保存ができる。Usersの共有(shared)にも保存できる。
    • 選択した保存先は、アプリケーションを終了しても記憶されている。
    • XcodeのコンソールエリアにFailed getting container for URL…というエラーが出る。
  2. AppSandBox設定のみ
    -[NSVBSavePanel init] caught non-fatal NSObjectNotAvailableException…というExceptionが出て、ファイルも保存できない。

  3. AppSandBox設定プラスUser Selected Fileの読み書き許可

    • 書類(Documents)フォルダがデフォルトの保存先になっている。
    • ファイルの保存ができる。Usersの共有(shared)にも保存できる。
    • 選択した保存先は、アプリケーションを終了しても記憶されている。
  4. 上記の条件でmyLogFileというフォルダにファイルを保存、そのフォルダをFinderで別の場所に移動

    myLogFileの記憶は失われ、デフォルトの書類(Documents)フォルダにリセットされる。

  5. NSURLのブックマーク機能の追加
    UserDefaultsに、ユーザーが選択したログファイル保存先のURLではなく、bookmarkに変換したものを保存し、それを解読してsetDirectoryURLするように変更した。bookmarkはsecurity-scopedではない、普通のものである。

    改造したコード
    - (IBAction)save:(id)sender {
    
        NSString *logFile = @"A Test String to save LogFile.";
    
        NSString *aLogDirectory = nil;
        NSData *aBookMark = nil;
        NSData *bBookMark = nil;
        NSURL *aBookmarkedURL = nil;
        NSURL *bLogDirectoryURL = nil;
        NSError *anError;
        BOOL isDir;
        NSFileManager *aManager = [NSFileManager defaultManager];
    
        // Set default directory
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docDir = [paths objectAtIndex:0];
    
        aLogDirectory = docDir;
    
        aBookMark = [defaults objectForKey:@"logBookMark"];
    
        if (aBookMark.length != 0) {
    
            aBookmarkedURL = [NSURL URLByResolvingBookmarkData:aBookMark
                                                       options:0
                                                 relativeToURL:nil
                                           bookmarkDataIsStale:nil
                                                         error:&anError];
    
            if (aBookmarkedURL != NULL) {
    
                aLogDirectory = [aBookmarkedURL path];
    
                if ([aManager fileExistsAtPath:aLogDirectory isDirectory:&isDir]) {
    
                    if (!isDir) aLogDirectory = docDir;
                }
    
            } else {
    
                if (anError) {
    
                    NSLog(@"Error resolving bookmark: %@",anError);
                    anError = nil;
                    return;
                }
            }
        }
    
        NSDateFormatter *format = [[NSDateFormatter alloc] init];
        [format setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]];
        [format setDateFormat:@"yyyyMMdd-HHmmss"];
    
        NSString *stringTime = [format stringFromDate:[NSDate date]];
    
        NSString *logFileName = [NSString stringWithFormat:@"Wing Go Client Log - %@",stringTime];
    
        NSSavePanel *savePanel = [NSSavePanel savePanel];
        [savePanel setAllowedFileTypes:[NSArray arrayWithObject:@"txt"]];
        [savePanel setAllowsOtherFileTypes:NO];
        [savePanel setNameFieldStringValue:logFileName];
        [savePanel setDirectoryURL:[NSURL fileURLWithPath:aLogDirectory]];
        [savePanel setMessage:NSLocalizedString(@"Select the place to save Logfile",
                                                @"Select the place to save Logfile")];
    
        if ([savePanel runModal] == NSFileHandlingPanelOKButton) {
    
            NSURL *selectedURL = [savePanel URL];
            NSString *selectedFile = [selectedURL path];
    
            BOOL logSuccess= [logFile writeToFile:selectedFile
                                       atomically:YES
                                         encoding:NSUTF8StringEncoding
                                            error:&anError];
    
            if(!logSuccess) {
    
                //Don’t look at an NSError unless you konw that something failed!
                if (anError) {
    
                    NSLog(@"Error creating file: %@",anError);
                    anError = nil;
                    return;
                }
    
            }
    
            bLogDirectoryURL = [NSURL fileURLWithPath:[selectedFile stringByDeletingLastPathComponent]];
    
            bBookMark = [bLogDirectoryURL bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile includingResourceValuesForKeys:nil relativeToURL:nil error:&anError];
    
            if (bBookMark != NULL) {
    
                [defaults setObject:bBookMark forKey:@"logBookMark"];
                [defaults synchronize];
    
            } else {
    
                if (anError) {
    
                    NSLog(@"Error creating Bookmark: %@",anError);
                    anError = nil;
                    return;
                }
            }
        }
    }
    • 書類(Documents)フォルダがデフォルトの保存先になっている。(そう設定した)
    • ファイルの保存ができる。Usersの共有(shared)にも保存できる。
    • 選択した保存先は、アプリケーションを終了しても記憶されている。
    • 保存先のフォルダを移動しても追従する。

「なるほど、なるほど。」と言いたいところだが、
Usersの共有(shared)フォルダというのは、AppSandBoxが用意するアプリケーション専用のコンテナ(砂場)の外ではないのか?
security-scoped bookmarkを使うのはどんな条件なんだ?
という疑問が残る。
Appleの公式ドキュメントを読んでも、頭が悪い筆者には理解できなかった。誰か教えて欲しい!!

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この投稿へのトラックバック

トラックバックはありません。

トラックバック URL