App-scoped bookmarks

Hi all,

Does anyone have experience with app-scoped bookmarks for sandboxed apps? I need repeated access to a folder (user-defined) outside of my app’s container file, and I’d like to not have to ask the user to “select” the output folder each time the app is launched.

Currently, I am having the user “select” the output folder using something like this:


set outputFolder to choose folder default location (path to library folder from user domain)

This works fine and all, until the user quits the app. How can I convert the path resulting from the “choose folder” panel into an app-scoped bookmark? Thanks in advance for any guidance/help!

The first step is to abandon choose file and use an NSOpenPanel. From that you can get an NSURL, from which you can make a security-scoped bookmark by calling bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:.

Ok. I figured such was the case…


 on setOutputFolder_(sender)

        set outputFolder to current application's NSOpenPanel's openPanel()
        outputFolder's setCanChooseFiles_(0)
        outputFolder's setCanChooseDirectories_(1)
        outputFolder's setAllowsMultipleSelection_(0)
        outputFolder's runModal()
        set outputFolderURL to outputFolder's URLs()

    end setOutputFolder_

Now that I’ve got the URL set as outputFolderURL, I think I need to do the following:

  • convert this URL into a bookmark
  • save this bookmark to a plist
  • run some applescript while I have access to the user-specified folder
  • close access to the bookmark/URL
  • recall the bookmark as needed (when app is relaunched) and run some more AppleScript

I think I can handle setting up the entitlements for this but I’ll be honest, I have no idea how to call bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:

FWIW, I’ve looked over the Apple documentation on these bookmarks and searched through this forum for some examples, but it’s all way over my head.

Thank you for your help!!

Something like should get you started:

set aURL to outputFolderURL's objectAtIndex_(0)
set bookmarkData to aURL's bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(NSURLBookmarkCreationWithSecurityScope, missing value, missing value, missing value)

I am trying to set up the security bookmark as follows:

        set outputFolder to current application's NSOpenPanel's openPanel()
        outputFolder's setCanChooseFiles_(0)
        outputFolder's setCanChooseDirectories_(1)
        outputFolder's setAllowsMultipleSelection_(0)
        outputFolder's runModal()
        set outputFolderURL to outputFolder's URLs()

        set aURL to outputFolderURL's objectAtIndex_(0)
        set bookmarkData to aURL's bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(NSURLBookmarkCreationWithSecurityScope, missing value, missing value, missing value)

but am getting the following error:

I have included com.apple.security.files.bookmarks.app-scope and com.apple.security.files.user-selected.read-write in my entitlements. What am I missing here? Does NSURLBookmarkCreationWithSecurityScope need to be defined somewhere?

EDIT:

I changed the first line to include “current application’s” and am now not getting an error, but to be honest, I’m not sure if this is correct:

        set bookmarkData to aURL's bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(current application's NSURLBookmarkCreationWithSecurityScope, missing value, missing value, missing value)

set bookmarkURL to NSURL's URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, current application's NSURLBookmarkResolutionWithSecurityScope, missing value, missing value, missing value)


Including “current application’s” is correct.

Alright. I’ve made quite a bit of progress on this with the help of Shane. Thank you for always being willing to help, Shane!

I’m at the point now of trying to start access the security scoped bookmark, but not sure how to really do that. I’m sure the two lines below marked with the comment “-- THIS” are completely wrong…

       
 set bookmarkData to NSUserDefaults's standardUserDefaults()'s objectForKey_("SecurityBookmark")
        
set bookmarkURL to NSURL's URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, current application's NSURLBookmarkResolutionWithSecurityScope, missing value, missing value, missing value)
        
bookmarkURL's startAccessingSecurityScopedResource() -- THIS 
        
set outputFolder to current application's NSOpenPanel's openPanel()
outputFolder's setCanChooseFiles_(0)
outputFolder's setCanChooseDirectories_(1)
outputFolder's setAllowsMultipleSelection_(0)
outputFolder's setDirectoryURL_(current application's NSURL's URLWithString_(bookmarkURL)) -- THIS
outputFolder's runModal()

I also am wondering if I need to initialize the variable bookmarkURL differently. Currently, I have it initialized as property bookmarkURL : missing value but should I instead be initializing it as a NSURL ?

[NSURL length]: unrecognized selector sent to instance 0x608000662980 (error -10000)

EDIT:

Got it working by changing:
outputFolder’s setDirectoryURL_(current application’s NSURL’s URLWithString_(bookmarkURL))

To:
outputFolder’s setDirectoryURL_(bookmarkURL)

What are you concerned about here?

Surely the argument should simply be bookmarkURL. But even if bookmarkURL were a string, you would make an NSURL using fileURLWithPath:, not URLWithString:, which is for non-file URLs.

It makes no difference.

Probably because you’re trying to treat bookmarkURL as a string.

Shane,

Thanks again for your help. I realized the URLWithString error as I was walking home from work. Got home, fixed it and tested, came back here to update and found you’d already beaten me to the correction! I felt like a really dummy for not catching that… it’s been a long week.

Anyways, with the bookmarkURL’s startAccessingSecurityScopedResource() line, I thought maybe I needed to pass a boolean to it, like true or whatnot.

For anyone who is interested, I wanted to share the code I worked on while trying to figure out app-scoped bookmarks. Many thanks to all who helped me with it. Here’s the ‘final’ code where the bookmark is created, stored, retrieved, and then tested:

 

    property NSURL : class "NSURL"
    property bookmarkData : class "NSDATA"
    property NSUserDefaults : class "NSUserDefaults" of current application
    property securityBookmark : missing value


on setupBookmark_(sender) -- LINKED TO BUTTON IN IB

        -- GETTING THE PATH TO THIS APPLICATION'S CONTAINER FOLDER.
        set appContainerLibraryPath to (path to library folder from user domain) as text
        
        -- REMOVING THE CONTAINERS FOLDER FROM THE PATH (RESULTS IN A PATH TO USER'S LIBRARY).
        set AppleScript's text item delimiters to ":Containers"
        set userLibraryPath to text item 1 of appContainerLibraryPath
        set userLibraryPath to POSIX path of userLibraryPath
        set AppleScript's text item delimiters to ""

        -- ASK THE USER TO SELECT THEIR USER LIBRARY FOLDER.
        -- APPLESCRIPT'S 'CHOOSE FOLDER' IS USED BECAUSE IT IS IDEAL FOR THE USER TO NOT HAVE TO GO "SEARCHING" FOR THEIR USER LIBRARY.
        -- SINCE THE SECURITY SCOPED BOOKMARK IS NOT YET SET UP, setDirectoryURL CANNOT BE USED WITH NSOpenPanel AND THUS THE USER'S LIBRARY CANNOT BE PRE-SELECTED
        -- USING APPLESCRIPT'S 'CHOOSE FOLDER' THE USER'S LIBRARY CAN BE PRE-SELECTED, MAKING IT EASIER FOR THE USER TO COMPLETE THE TASK
        set chosenFolderPath to (choose folder default location userLibraryPath with prompt "This application needs to know where your user Library folder is." & return & return & "Please select the Library folder which has already been highlighted." without invisibles) as Unicode text
        
        -- CONVERT THE USER SELECTED FOLDER TO A URL.
        set chosenFolderPath to (POSIX path of chosenFolderPath)
        set chosenURL to current application's class "NSURL"'s URLWithString_("file://" & chosenFolderPath)

        -- CREATE A SECURITY BOOKMARK WITH THE URL.
        set bookmarkData to chosenURL's bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(current application's NSURLBookmarkCreationWithSecurityScope, missing value, missing value, missing value)
        
        -- SAVE THE BOOKMARK TO USER DEFAULTS.
        NSUserDefaults's standardUserDefaults()'s setObject_forKey_(bookmarkData, "SecurityBookmark")
        
    end setupBookmark_
    
    
    
    on testBookmark_(sender) -- LINKED TO BUTTON IN IB
        
        -- RETRIEVE THE SECURITY BOOKMARK FROM USER DEFAULTS.
        set bookmarkData to NSUserDefaults's standardUserDefaults()'s objectForKey_("SecurityBookmark")
        
        -- GET THE URL FROM THE SECURITY BOOKMARK.
        set bookmarkURL to NSURL's URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, current application's NSURLBookmarkResolutionWithSecurityScope, missing value, missing value, missing value)

        -- START ACCESS TO THE BOOKMARKED PATH.
        bookmarkURL's startAccessingSecurityScopedResource()
        
        -- CONVERT THE URL BACK TO A POSIX PATH:
        tell bookmarkURL to set bookmarkPath to |path|()
        set bookmarkPath to bookmarkPath as Unicode text
        set bookmarkPath to POSIX path of bookmarkPath
        
        try -- TESTS THIS APPLICATION'S ABILITY TO CREATE A FILE AT THE BOOKMARK'S PATH
            do shell script "touch " & bookmarkPath & "/testTouch.txt"
            log "Successful touch at " & bookmarkPath
            do shell script "rm " & bookmarkPath & "/testTouch.txt"
        on error
            log "Unsuccessful touch at " & bookmarkPath
        end try
        
        -- TESTS THE APPLICATION'S ABILITY TO USE THE BOOKMARK IN A NSOpenPanel.
        -- SETTING UP THE NSOpenPanel.
        set chosenFolderPath to current application's NSOpenPanel's openPanel()
        chosenFolderPath's setCanChooseFiles_(0)
        chosenFolderPath's setCanChooseDirectories_(1)
        chosenFolderPath's setAllowsMultipleSelection_(0)
        chosenFolderPath's setDirectoryURL_(bookmarkURL)
        
        -- SHOWING THE NSOpenPanel.
        chosenFolderPath's runModal()
        
        -- STOP ACCESS TO THE BOOKMARKED PATH.
        bookmarkURL's stopAccessingSecurityScopedResource()

    end testBookmark_