Hi all,
I’m having trouble with security-scoped bookmarks in a sandboxed app.
I have an object which has a URL as a property, being a folder the user chooses
to save stuff to. This object is persistent, due to NSCoding. When I archive
the URL, I use a security-scoped bookmark to save the data. When I dearchive, I
resolve the security-scoped bookmark to recover the folder URL. Before I start
to write files to this folder, I use the -startAccessingSecurityScopedResource,
and when I’m done writing the files, I balance that with a call to
-stopAccessingSecurityScopedResource. The app has the necessary entitlements
for user selected files, and for app-scope bookmarks.
The problem reveals itself only over a series of launches, which cause the
object above to be archived and dearchived.
Launch 1:
Normal. The user chooses a new Folder using the sandboxed NSOpenPanel. Files
are written to this folder normally.
Launch 2:
The archived bookmark data is resolved and the URL is restored. Access to the
resource appears to be normal (returns YES) and the files are written. Shortly
afterwards the following is written to the console:
2016-09-07 13:34:48.052 MyApp[8602:1574205] CFStringRef
__CFPasteboardIssueSandboxExtensionForPath(CFStringRef) : sandbox extension
creation failed: client lacks entitlements? for path:
[/Users/grahamcox/Desktop/Exports/Snorkel0001-16.jpg]
2016-09-07 13:34:48.052 MyApp[8602:1574205] CFDataRef
__CFPasteboardCreateSandboxExtensionDataFromCFData(CFDataRef) : failed to
obtain sandbox extension data for url
[file:///Users/grahamcox/Desktop/Exports/Snorkel0001-16.jpg]
It’s not clear where this message is being posted from - my code has finished
writing the files (successfully) and has called
-stopAccessingSecurityScopedResource before this is emitted. The URL in the
message is the file I’ve written, and the folder that contains it is the one
the user chose previously. I do appear to have the necessary entitlements:
com.apple.security.files.bookmarks.app-scope YES
com.apple.security.files.user-selected.read-write YES
Once this has occurred, when the object is archived, the method
-bookmarkDataWithOptions:… fails, with the error:
Error Domain=NSCocoaErrorDomain Code=256 "Could not open() the item"
UserInfo={NSURL=file:///Users/grahamcox/Desktop/Exports/,
NSDebugDescription=Could not open() the item}
If this occurs, my code skips the saving of the bookmark data, which is nil
anyway. That leaves the archive without an entry for the URL.
Launch 3:
Because there’s no bookmark data, on dearchiving the URL is reverted to a
default one that is within the user’s ‘Documents’ folder, which the app can
write to. The call to -startAccessingSecurityScopedResource with this default
URL returns NO, but files are written normally without any problems. The
default URL is obtained by:
NSURL* docFolder = [[NSFileManager defaultManager]
URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask
appropriateForURL:nil create:YES error:&error];
And then appending a default subfolder to it (and creating it if necessary).
This URL subsequently gets archived as a security-scoped bookmark as normal.
Subsequent launches with this folder URL work fine.
So the issue seems to be with the state in Launch 2, that, despite appearing to
work, spits out some console messages from somewhere, and then fails to create
the bookmark data. AFAICS, I’m using all the correct APIs and option flags.
The code:
- (instancetype) initWithCoder:(NSCoder*) aDecoder
{
self = [super init];
if( self )
{
//[other dearchived properties omitted for brevity]
NSData* bookmark = [aDecoder
decodeObjectForKey:@"Exp_folderBookmark"];
NSError* error = nil;
BOOL stale = NO;
self.folderURL = [NSURL URLByResolvingBookmarkData:bookmark
options:NSURLBookmarkResolutionWithoutUI |
NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil
bookmarkDataIsStale:&stale error:&error];
if( self.folderURL )
{
if( stale )
{
bookmark = [self.folderURL
bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
}
}
else
{
NSLog(@"saved URL could not be resolved, restoring to
default ~/Documents/<app name>/Exported Files/ error=%@", error );
self.folderURL = [MDABExportController
defaultExportLocation];
}
}
return self;
}
- (void) encodeWithCoder:(NSCoder*) aCoder
{
//[other archived properties omitted for brevity]
NSError* error = nil;
NSData* bookmark = [self.folderURL
bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if( bookmark )
[aCoder encodeObject:bookmark forKey:@"Exp_folderBookmark"];
else
{
NSLog(@"unable to archive URL bookmark data for Export
operation, error=%@", error);
}
}
This kind of thing has always worked for me in the past, so I’m not sure why
this particular case is giving me these problems.
Can anyone suggest anything I’ve overlooked?
—Graham
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]