Access files in an isolated Mac application using swift

I am working on an application for OS X 10.9 with a quick version of the sandbox.

The application requires access to the SQLite database file. I let the user select / open the file using NSOpenPanel. Then I save the file path using NSUserDefaults for future reference.

I want this file to open automatically every time the application starts again. I get the saved path from NSUserDefault, but when I open the file with this path, I get an error saying that I do not have permission to access the file.

(works without sandbox)

Bookmarks seem to be the solution to my problem.

Is there a good tutorial on how to use bookmarks with Swift for an OSX application? Any other suggestion?

+8
source share
2 answers

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.

+10
source

Security-protected bookmarks are exactly what you need. A good starting point is the apple documentation on the AppStore sandbox (which includes sample code) and a link to the NSFileManager class.

you will not save the path in user defaults other than binary bookmark data.

0
source

All Articles