Security-scoped bookmarks, what's going on?
Security-scoped bookmarks, what's going on?
- Subject: Security-scoped bookmarks, what's going on?
- From: Graham Cox <email@hidden>
- Date: Wed, 07 Sep 2016 13:59:40 +1000
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@hidden)
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:
This email sent to email@hidden