Share security scoped bookmark in app group?

1) Main app and extension register for app groups.


2) Main app gets a url from NSOpenPanel, makes a security scoped bookmark and saves is in NSUserdefaults with group container as the suite name.


3) Run the extension, grab the bookmark data from user defaults and resolve it. The data is not nil, but fails to resolve:


Error Domain=NSCocoaErrorDomain Code=259 "The file couldn’t be opened because it isn’t in the correct format."

Accepted Reply

My bug report was closed as not a bug. Figured I'd let everyone know so they don't get 😠

Replies

The data is not nil, but fails to resolve:

Three things:

  • Do you have the same sandbox settings on your app and your extension? Of specific concern is

    com.apple.security.files.user-selected.read-only
    .
  • Presumably you create the bookmark with

    -bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:
    . What options do you pass in?
  • Have you logged the bookmark data to make sure you’re getting back the right value? You can do that with a simple

    NSLog
    :
    NSLog(@"bookmark = %@", bookmarkData);

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes the bookmark data is the same when logging it out if I run the same code from the main app or the app extension. The difference is the main app resolves it without error but the extension fails with the error.


I make the bookmark like this:


  NSURL *url = openPanel.URL;

        NSError *error = nil;
        NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                                   includingResourceValuesForKeys:nil
                                                    relativeToURL:nil
                                                            error:&error];
        if (bookmark != nil)
        {
            NSUserDefaults *userDefaults = [[NSUserDefaults alloc]initWithSuiteName:MY_GROUP_ID_HERE];
            [userDefaults setObject:bookmark forKey:USER_DEFAULTS_BOOKMARK_KEY_HERE];
            [userDefaults synchronize];
       }
      else
      {
              //check the error
      }



And I resolve the bookmark:


     urlFromBookmark = [NSURL URLByResolvingBookmarkData:bookmarkData
                                                   options:NSURLBookmarkResolutionWithSecurityScope
                                             relativeToURL:nil
                                       bookmarkDataIsStale:&isStale
                                                     error:&error];


The app and the app extension import the same file, and are using the exact same function to resolve the bookmark. Also I have com.apple.security.files.user-selected.read-write to YES in entitlements.

This also logs out, seems related?


Failed to read values in CFPrefsPlistSource<0x6080000ee380> (Domain: GroupIDIsHere, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null)): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd

This is weird, if I pass in 0 instead of NSURLBookmarkResolutionWithSecurityScope and NSURLBookmarkCreationWithSecurityScope my extension can resolve the bookmark, and it works.


I always thought if you are sandboxed you had to make security scoped bookmarks. Is this a bug, or are my assumptions about the API wrong? Perhaps I should take another look at the documentation.

Documentation seems to imply that sandboxed apps need to use security scoped bookmarks, unless I'm looking in the wrong place (app sandbox guide).


I went through the trouble of turning off automatic code signing and setting up provisioning profiles for main app & the extension manually (thinking maybe this is a code signing issue), but I get the same results.


The fact that specifying 0 as the option for both creating and resolving the bookmark works is fine for me...I'm just hesitant to go forward with this and release because I wouldn't be surprised if an OS update broke my app (if this is indeed a bug?)

Just googled around...found a related stackoverflow post:


h t t p :/ / stackoverflow.com/questions/37897118/using-security-scoped-bookmark-in-finder-sync-extension-with-user-default


No answer thouugh.

Also I have com.apple.security.files.user-selected.read-write to YES in entitlements.

Did you just look in the

.entitlements
file? Or did you check the build binary? What does this show?
$ codesign -d --entitlements :- /path/to/Your.app/Contents/PlugIns/YourExtension.appex

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The output looks correct when running codesign -d -entitlements on the .appex


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-/

<plist version="1.0">

<dict>

<key>com.apple.security.app-sandbox</key>

<true/>

<key>com.apple.security.application-groups</key>

<array>

<string>APP_GROUP_IS_HERE</string>

</array>

<key>com.apple.security.files.bookmarks.app-scope</key>

<true/>

<key>com.apple.security.files.bookmarks.document-scope</key>

<true/>

<key>com.apple.security.files.user-selected.read-write</key>

<true/>

</dict>

</plist>

Interesting. Honestly, I’m not sure what’s going on. If you want to drive this to a conclusion, you should open a DTS tech support incident so that you can talk to DTS’s expert on this sort of thing.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for the response. I filed a bug on this, just in case.

28960392

Also noticed something interesting. As I mentioned, passing in 0 for the create/resolve option strangely works, but if I restart my Mac and run, the bookmark will only resolve in the main app and not the extension, so going forward with this clearly is not an option for an app release.

My bug report was closed as not a bug. Figured I'd let everyone know so they don't get 😠

Working on the same thing, I also ran into this. Did you manage to solve it?

I think I got this working using the following recipe:


0) setup "group app" and appropriate entitlements, think of:

com.apple.security.application-groups

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

com.apple.security.files.user-selected.read-write


1) In the UI-app, use the NSOpenPanel to obtain a directory-url.

2) Use url.bookmarkData with the option .minimalBookmark and save this data to the user defaults of the group (using UserDefaults(suiteName: "groupname") for example). This will store a regular bookmark in the userdefaults of the group-container.

3) Do NOT close the UI-app yet, but first run the command-line app, read the data from the group-defaults and using the data resolve the url BookmarkData using the option .withoutUI

4) Then, in order to persist the use of the url in the Helper command-line app, create a security scoped url using url.bookmarkData with the option .withSecurityScope

5) Write the data to the local user defaults of the command-line app, and next time, read the data from the user defaults of the command-line app and resolve the url from data with the option .withSecurityScope

6) use: let succeeded: Bool = permissionURL.startAccessingSecurityScopedResource()

The boolean will be true.


So, note an important thing: you can NOT create a security scoped bookmark in the main app, write it to the group, read that from a helper command-line utility and resolve it. You will get: "error: The file couldn’t be opened because it isn’t in the correct format."

But a normal Bookmark works while both programs are running, which gives you a chance to read the normal Bookmark from the group-defaults and write the security scoped Bookmark to the userdefaults of the Helper-app (which needs to be written by the Helper-app, NOT by the main UI-app).

>So, note an important thing: you can NOT create a security scoped bookmark in the main app, write it to the group, read that from a helper command-line utility and resolve it. You will get: "error: The file couldn’t be opened because it isn’t in the correct format."

But a normal Bookmark works while both programs are running, which gives you a chance to read the normal Bookmark from the group-defaults and write the security scoped Bookmark to the userdefaults of the Helper-app (which needs to be written by the Helper-app, NOT by the main UI-app).


Interesting. At the time, I recall only being able to resolve non -security scooped bookmarks (like you discovered) from the non-main app. I didn't think to try what you suggested because the other app in the group is an app extension and I didn't control the lifecycle of it. If I remember correctly the response I got in my bug report was dismissive and seemed to indicate that they really didn't want app groups doing this. Could have misinterpreted though.


Eventually I was able to redesign the app to accomplish the same thing without having to bookmark at all from the extension. I think I set a flag to signify whether or not the URL was bookmarked in the main app, so the extension didn't know what URL was bookmarked, just *if* there was a url bookmarked. THen I either posted a distributed notification from the extension, which the main app picked up and performed the action using the bookmark or I declared a Service and use the NSPerformService function. I can't remember right now which one I did though, was awhile ago.

I'm seeing this same issue, attempting to share a Security Scoped Bookmark from a READ-WRITE main app to a READ-ONLY App Extension.


Changing the main app to have READ-WRITE file access seems to have broken it for me; I believe it was working fine when both App Group members were set to READ-ONLY file access. I'm going to test that now.


Getting the same error:



[User Defaults] Couldn't read values in CFPrefsPlistSource<0x6000000eb380> (Domain: group.com.company.MainApp, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd


and


Error Domain=NSCocoaErrorDomain Code=259 "The file couldn’t be opened because it isn’t in the correct format."