Sandbox allows or denies user-selected files to be saved

Two classes in my sandboxed application need to export a NSDictionary as a plist file.

On the Capabilities, the Permission/Access setting is read/Write for User selected files. I use XCode 10.11.1 on Mojave, and build for ≥10.12. From several classes that need to export files, two are very similar and manage NSDictionary.

For those two, when the user prompts for save, I open a NSSavePanel to choose the destination filename and directory. I use exactly the same code for my two classes:
Code Block
NSSavePanel *panel = [NSSavePanel savePanel];
[panel setNameFieldStringValue:newFilename];
panel.extensionHidden = TRUE;
[panel beginWithCompletionHandler:^(NSInteger result) {
if (result == NSFileHandlingPanelOKButton) {
NSURL *saveURL = [panel URL];
NSString *savePath = [saveURL path];
if (![[savePath pathExtension] isEqual:@"plist"]){
savePath = [savePath stringByDeletingPathExtension];
savePath = [savePath stringByAppendingString:@".plist"];
}
[exportDic writeToFile:savePath atomically:YES];
}
}];

What is strange is that saving this ways always works for one class that use this code, whereas it doesn't succeed for the second class, except one non-reproductible time! I got no message on the debugger console, but I can see one in the Console application:
Code Block
Sandbox: MyApp (xxxx) deny(1) file-write-create my_file_path.plist

I verified everything, reset the sandbox setting, clean build folder, quit/relaunched Xcode, and even rebooted the computer several times. Any idea? Thanks

It’s likely that lines 8 through 11 are the problem here. When the save panel gives you back a URL it extends your sandbox so that you can access exactly that URL. You are not allowed to change the URL in any way, and that includes ‘fixing’ the path extension.

I got no message on the debugger console

Right, because you’re not actually checking for errors from -writeToFile:atomically:. If you were, it’s likely that you’d see it return NO. And if you updated to its non-deprecated replacement, -writeToURL:error:, you’d get an NSError that explains the problem in more detail.

IMPORTANT Apple has a general policy of moving away from file paths to file URLs. This is because file URLs can carry extra payload, something that’s especially important when dealing with the sandbox. For example, a file URL can include the security scope that’s necessary to call -startAccessingSecurityScopedResource. I encourage you to follow Apple’s lead here and stop using file paths.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Thanks for your reply. So, there is no way, in a sandboxed app, to perform an extension check? It's a pity as it denies to the app the right to prevent a user typo. In this case, no other compatible application will be able to open the file directly, unless the user corrects itself the extension in the Finder…

What I don't understand it that this code works in one of my classes… So why are the Sandbox rules considered as violated in a class, and not in another class using the same code?

So, there is no way, in a sandboxed app, to perform an extension check?

You can perform the check, you just can’t ‘fix’ it. You could, if you wanted to, refuse to save the file with the wrong extension. Honestly though, I think that’d be doing your user a disservice. It makes sense to set up the default extension but if the user chooses to override that it’s really their business.

So why are the Sandbox rules considered as violated in a class, and
not in another class using the same code?

The App Sandbox applies equally to all code running inside your process, so it’s not clear why this is working in one case and not the other. Are you sure that this extension changing is running in both case?

Oh and before you dig into this further make sure to update your code to use URLs than paths. As I mentioned earlier, URLs carry an extra payload that’s important when working in the sandbox.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Sandbox allows or denies user-selected files to be saved
 
 
Q