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