12
votes

I'm converting my Lion app to use the App Sandbox. I'm trying to make use of the security-scoped bookmarks feature introduced in 10.7.3 to allow persistent access to a folder. The code I have below returns a nil bookmark, and produces the following log message: XPC couldn't look up the Mach service for scoped bookmarks agent.

I set the User Selected File Access entitlement to Read/Write Access, and also tried with and without the surrounding ..AccessingSecurityScopedResource calls.

I think I'm doing everything right according to the documentation, so I'd appreciate any pointers. The code was working to retrieve a plain URL before I began sandboxing the app.

NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:NO];
[openPanel setCanChooseDirectories:YES];
[openPanel setAllowsMultipleSelection:NO];

NSInteger result = [openPanel runModal];

if( result == NSFileHandlingPanelCancelButton ) {
    return;
}

NSArray *urls = [openPanel URLs];

if( urls != nil && [urls count] == 1 ) {
    NSURL *url = [urls objectAtIndex:0];

    NSData *bookmark = nil;
    NSError *error = nil;
    bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
             includingResourceValuesForKeys:nil
                              relativeToURL:nil // Make it app-scoped
                                      error:&error];
    if (error) {
        NSLog(@"Error creating bookmark for URL (%@): %@", url, error);
        [NSApp presentError:error];
    }

    NSLog(@"bookmark: %@", bookmark);
}

Update (x3)

Now that I got it working, I can verify that the calls to -startAccessingSecurityScopedResource and -stopAccessingSecurityScopedResource are not necessary in the code above, since the Powerbox grants access to the resource after the user selects it in the NSOpenPanel.

If you're creating a bookmark from another security-scoped URL, such as making a document-scoped bookmark from an app-scoped bookmark created in another app session, then you need to get access to the file first.

1
The start/stop calls are never necessary when making a bookmark. You already got access from NSOpenPanel. The bookmark is saving that access for future runs. And those future runs are where you're going to need to call startAccessingSecurityScopedResource (on the URL you get back from resolving the bookmark).abarnert
@abarnert I believe you're right, since in the code I posted I use an NSOpenPanel, but you do need it if you're creating a bookmark from a source other than an NSOpenPanel or NSSavePanel (such as making a new bookmark from an existing one to change its scope). I've updated my post.Dov
Documentation made my eyes bleed 'til I found "security-scoped bookmarks" and this is really helpful sample code, too. I don't have the power to create a tag, might I suggest we add a tag "security-scoped bookmark"?Mark

1 Answers

10
votes

It turns out I was missing a crucial entitlement, not listed in the UI, but listed in the documentation:

com.apple.security.files.bookmarks.app-scope

Update 12/18/2018

According to this Twitter thread, this entitlement may not be required anymore. Thanks @pkamb for alerting me to this.