The App Sandbox design guide says:
The related items feature of App Sandbox lets your app access files that have the same name as a user-chosen file, but a different extension. This feature consists of two parts: a list of related extensions in the application’s Info.plist file and code to tell the sandbox what you’re doing.
My Info.plist defines a document type for .pnd
files (the user-chosen file), as well as a document type for .bak
files. The entry for the .bak
files has, among other properties, the property NSIsRelatedItemType = YES
.
I am trying to use Related Items to move an existing file to a backup file (change .pnd suffix to .bak suffix) when the user writes a new version of the .pnd file. The application is sandboxed. I am not proficient with sandboxing.
I am using PasteurOrgManager
as the NSFilePresenter
class for both the original and backup files:
@interface PasteurOrgData : NSObject <NSFilePresenter>
. . . .
@property (readonly, copy) NSURL *primaryPresentedItemURL;
@property (readonly, copy) NSURL *presentedItemURL;
@property (readwrite) NSOperationQueue *presentedItemOperationQueue;
@property (readwrite) NSFileCoordinator *fileCoordinator;
. . . .
- (void) doBackupOf: (NSString*) path;
. . . .
@end
The doBackupOf:
method is as follows. Notice that it also sets the NSFilePresenter
properties:
- (void) doBackupOf: (NSString*) path
{
NSError *error = nil;
NSString *appSuffix = @".pnd";
NSURL *const pathAsURL = [NSURL URLWithString: [NSString stringWithFormat: @"file://%@", path]];
NSString *const baseName = [pathAsURL lastPathComponent];
NSString *const prefixToBasename = [path substringToIndex: [path length] - [baseName length] - 1];
NSString *const baseNameWithoutExtension = [baseName substringToIndex: [baseName length] - [appSuffix length]];
NSString *backupPath = [NSString stringWithFormat: @"%@/%@.bak", prefixToBasename, baseNameWithoutExtension];
NSURL *const backupURL = [NSURL URLWithString: [NSString stringWithFormat: @"file://%@", backupPath]];
// Move backup to trash — I am sure this will be my next challenge
// (it's a no-op now because there is no pre-existing .bak file)
[[NSFileManager defaultManager] trashItemAtURL: backupURL
resultingItemURL: nil
error: &error];
// Move file to backup
primaryPresentedItemURL = pathAsURL;
presentedItemURL = backupURL;
presentedItemOperationQueue = [NSOperationQueue mainQueue];
[NSFileCoordinator addFilePresenter: self];
fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter: self]; // error here
[self backupItemWithCoordinationFrom: pathAsURL
to: backupURL];
[NSFileCoordinator removeFilePresenter: self];
fileCoordinator = nil;
}
The backupItemWithCoordinationFrom:
method does the heavy lifting, basically:
[fileCoordinator coordinateWritingItemAtURL: from
options: NSFileCoordinatorWritingForMoving
error: &error
byAccessor: ^(NSURL *oldURL) {
[self.fileCoordinator itemAtURL: oldURL willMoveToURL: to];
[[NSFileManager defaultManager] moveItemAtURL: oldURL
toURL: to
error: &error];
[self.fileCoordinator itemAtURL: oldURL didMoveToURL: to];
}
but the code doesn't make it that far. I have traced the code and the URL variables are as I expect, and are reasonable. At the point of "error here" in the above code, where I allocate the File Presenter, I get:
NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 1
[presenter] +[NSFileCoordinator addFilePresenter:] could not get a sandbox extension. primaryPresentedItemURL: file:///Users/cope/Me.pnd, presentedItemURL: file:///Users/cope/Me.bak
Any help is appreciated.
(I have read related posts Where can a sandboxed Mac app save files? and Why do NSFilePresenter protocol methods never get called?. I have taken note of several other sandboxing-related posts that don't seem relevant to this issue.)
MacBook Pro, MacOS 10.13.5, XCode Version 9.3 (9E145)