Reusing Security Scoped Bookmark

reusing security scoped bookmark

I figured it out with NSUserDefaults.

var userDefault = NSUserDefaults.standardUserDefaults()
var folderPath: NSURL? {
didSet {
do {
let bookmark = try folderPath?.bookmarkDataWithOptions(.SecurityScopeAllowOnlyReadAccess, includingResourceValuesForKeys: nil, relativeToURL: nil)
userDefault.setObject(bookmark, forKey: "bookmark")
} catch let error as NSError {
print("Set Bookmark Fails: \(error.description)")
}
}
}

func applicationDidFinishLaunching(aNotification: NSNotification) {
if let bookmarkData = userDefault.objectForKey("bookmark") as? NSData {
do {
let url = try NSURL.init(byResolvingBookmarkData: bookmarkData, options: .WithoutUI, relativeToURL: nil, bookmarkDataIsStale: nil)
url.startAccessingSecurityScopedResource()
} catch let error as NSError {
print("Bookmark Access Fails: \(error.description)")
}
}
}

Reusing Apple Sandbox security-scoped bookmarks

It turned out I had failed to have balance between start and stop invokations. As the documentation of startAccessingSecurityScopedResource states, this can make the booksmarks invalid:

Warning: You must balance every call to the startAccessingSecurityScopedResource method with a corresponding call to the stopAccessingSecurityScopedResource method. If you fail to relinquish your access when you no longer need a file-system resource, your app leaks kernel resources. If sufficient kernel resources are leaked, your app loses its ability to add file-system locations to its sandbox, such as via Powerbox or security-scoped bookmarks, until relaunched.

I also ended up storing the bookmarks in NSUserDefaults instead of a file in the NSApplicationSupportDirectory folder.

Trouble creating Security-Scoped Bookmark

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.

macOS Security scoped URL bookmark for folder

You're resolving the security-scoped bookmark (for the directory) to let newUrl, but you call startAccessingSecurityScopedResource() on the file's URL fileURL. You need to call it for newURL.

newURL.startAccessingSecurityScopedResource()
// Decompressing fileURL with libarchive...
newURL.stopAccessingSecurityScopedResource()

Two more remarks:

  1. When obtaining access through NSOpenPanel, you don't need to call
    startAccessingSecurityScopedResource() and
    stopAccessingSecurityScopedResource(), because the user explicitly
    granted you access for this session.
  2. I use var isStale: ObjCBool = ObjCBool(false) instead. I'm no Swift expert, so not sure if var isStale = false is ok to use.

Xamarin.Mac Using security-scoped bookmarks

You are confusing some things. Your code

using (var dlg = NSSavePanel.SavePanel)
{
dlg.Message = AppResources.DialogMessageSaveEncryptedFileName;
dlg.AllowedFileTypes = new[] { "zip" };
dlg.Prompt = "Authenticate";

if (dlg.RunModal() > 0)
{
NSError error;
//NSData url = dlg.Url.CreateBookmarkData(NSUrlBookmarkCreationOptions.WithSecurityScope, null, null, out error);
NSData bookmark = dlg.Url.CreateBookmarkData(NSUrlBookmarkCreationOptions.WithSecurityScope, null, null, out error);
}
}

The dlg.Url is the url you want to access.
What you get back from dlg.Url.CreateBookmarkData() is the encoded bookmark data. You can store this data anyway you like to persist it across app launches. For example in UserDefaults
Storing:

NSUserDefaults.StandardUserDefaults["bookmark"] = bookmark; 
NSUserDefaults.StandardUserDefaults.Synchronize();

Later retrieving:

NSData bookmark = NSUserDefaults.StandardUserDefaults.DataForKey("bookmark");

This is the data you
put into NSUrl.FromBookmarkData to get the url back.

//NSData data = new NSData();
//NSUrl url = NSUrl.FromBookmarkData(data, NSUrlBookmarkResolutionOptions.WithSecurityScope, null, out bool isStale, out NSError error);
NSUrl url = NSUrl.FromBookmarkData(bookmark, NSUrlBookmarkResolutionOptions.WithSecurityScope, null, out bool isStale, out NSError error);
url.StartAccessingSecurityScopedResource();

//...

url.StopAccessingSecurityScopedResource();

Also if you need to keep using the access pay attention to the isStale return value from CreateBookmarkData. If it return true you need to refresh the bookmark from the url with url.CreateBookMarkData and replace the stored bookmark with the new one.

Security-Scoped Bookmarks for a directory

Your second error message tells you what is wrong - you haven't used a file:// URL.

This can be fixed by creating the URL properly from your path variable, however you will probably be better of sticking with URLs throughout and not doing the URL -> path -> URL transformation. All the operations you've used the path for can be done directly with URLs, just check the documentation for NSFileManager and NSURL. The only one which may be non-obvious is using NSURL's checkResourceIsReachableAndReturnError: rather than NSFileManager's fileExistsAtPath:, however read the documentation for checkResourceIsReachableAndReturnError: carefully and take its advice.

Making these changes should address at least three of the errors you have reported.

HTH

Document-scope, Security scoped bookmarks for file packages

I asked about this elsewhere and was told:

...we didn't implement support for it because it's complicated and there have been very few requests for it.

So that's that, you can't create this kind of bookmark because it's not implemented. I also filed a bug with Apple but the response just quoted the docs at me (i.e. telling me stuff I already knew and had mentioned in my report) before closing it. So, as of now and probably for the foreseeable future, this is not possible.



Related Topics



Leave a reply



Submit