Here is my answer that I just started working in Swift 3 with a little help from http://swiftrien.blogspot.com/2015/07/persisting-file-access-rights-between.html
import Foundation import Cocoa var bookmarks = [URL: Data]() func bookmarkPath() -> String { var url = app.applicationDocumentsDirectory url = url.appendingPathComponent("Bookmarks.dict") return url.path } func loadBookmarks() { let path = bookmarkPath() bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: path) as! [URL: Data] for bookmark in bookmarks { restoreBookmark(bookmark) } } func saveBookmarks() { let path = bookmarkPath() NSKeyedArchiver.archiveRootObject(bookmarks, toFile: path) } func storeBookmark(url: URL) { do { let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) bookmarks[url] = data } catch { Swift.print ("Error storing bookmarks") } } func restoreBookmark(_ bookmark: (key: URL, value: Data)) { let restoredUrl: URL? var isStale = false Swift.print ("Restoring \(bookmark.key)") do { restoredUrl = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) } catch { Swift.print ("Error restoring bookmarks") restoredUrl = nil } if let url = restoredUrl { if isStale { Swift.print ("URL is stale") } else { if !url.startAccessingSecurityScopedResource() { Swift.print ("Couldn't access: \(url.path)") } } } } func allowFolder() -> URL? { let openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.begin { (result) -> Void in if result == NSFileHandlingPanelOKButton { let url = openPanel.url storeBookmark(url: url!) } } return openPanel.url }
Swift 4 (update):
import Foundation import Cocoa var bookmarks = [URL: Data]() func fileExists(_ url: URL) -> Bool { var isDir = ObjCBool(false) let exists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) return exists } func bookmarkURL() -> URL { let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) let appSupportURL = urls[urls.count - 1] let url = appSupportURL.appendingPathComponent("Bookmarks.dict") return url } func loadBookmarks() { let url = bookmarkURL() if fileExists(url) { do { let fileData = try Data(contentsOf: url) if let fileBookmarks = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(fileData) as! [URL: Data]? { bookmarks = fileBookmarks for bookmark in bookmarks { restoreBookmark(bookmark) } } } catch { print ("Couldn't load bookmarks") } } } func saveBookmarks() { let url = bookmarkURL() do { let data = try NSKeyedArchiver.archivedData(withRootObject: bookmarks, requiringSecureCoding: false) try data.write(to: url) } catch { print("Couldn't save bookmarks") } } func storeBookmark(url: URL) { do { let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) bookmarks[url] = data } catch { Swift.print ("Error storing bookmarks") } } func restoreBookmark(_ bookmark: (key: URL, value: Data)) { let restoredUrl: URL? var isStale = false Swift.print ("Restoring \(bookmark.key)") do { restoredUrl = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) } catch { Swift.print ("Error restoring bookmarks") restoredUrl = nil } if let url = restoredUrl { if isStale { Swift.print ("URL is stale") } else { if !url.startAccessingSecurityScopedResource() { Swift.print ("Couldn't access: \(url.path)") } } } } func allowFolder() -> URL? { let openPanel = NSOpenPanel() openPanel.allowsMultipleSelection = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.canChooseFiles = false openPanel.begin { (result) -> Void in if result == NSFileHandlingPanelOKButton { let url = openPanel.url storeBookmark(url: url!) } } return openPanel.url }
To use this code, you first need to call NSOpenPanel so that the user can choose which folders to give you access to. The NSOpenPanel must be stored as a bookmark and saved to disk.
let url = allowFolder() saveBookmarks()
When you restart the application you must call
loadBookmarks()
then your application will have the same access level as when the user selects the folder. Hope this helps someone.
source share