File access in a sandboxed Mac app with swift

前端 未结 3 771
被撕碎了的回忆
被撕碎了的回忆 2021-02-02 01:14

I am working on an app for OS X 10.9 with swift, sandboxed.

The app needs access to a SQLite database file. I let the user choose/open a file with NSOpenPanel. I then sa

相关标签:
3条回答
  • 2021-02-02 01:28

    Here's more clean solution with SWIFT 5.0:

    import Foundation
    import Cocoa
    
    
        class BookmarkManager {
            static let manager = BookmarkManager()
            // Save bookmark for URL. Use this inside the NSOpenPanel `begin` closure
            func saveBookmark(for url: URL){
    
                guard let bookmarkDic = self.getBookmarkData(url: url),
                    let bookmarkURL = getBookmarkURL() else{
                        print("Error getting data or bookmarkURL")
                        return
                }
    
                do
                {
                    let data = try NSKeyedArchiver.archivedData(withRootObject: bookmarkDic, requiringSecureCoding: false)
                    try data.write(to: bookmarkURL)
                    print("Did save data to url")
                }
                catch
                {
                    print("Couldn't save bookmarks")
                }
            }
    
            // Load bookmarks when your app launch for example
            func loadBookmarks()
            {
    
                guard let url = self.getBookmarkURL() else {
                    return
                }
    
                if self.fileExists(url)
                {
                    do
                    {
                        let fileData = try Data(contentsOf: url)
                        if let fileBookmarks = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(fileData) as! [URL: Data]?
                        {
                            for bookmark in fileBookmarks{
                                self.restoreBookmark(key: bookmark.key, value: bookmark.value)
                            }
    
                        }
                    }
                    catch
                    {
                        print ("Couldn't load bookmarks")
                    }
    
                }
            }
    
            private func restoreBookmark(key: URL, value: Data){
                let restoredUrl: URL?
                var isStale = false
    
                Swift.print ("Restoring \(key)")
                do
                {
                    restoredUrl = try URL.init(resolvingBookmarkData: 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)")
                        }
                    }
                }
            }
            private func getBookmarkData(url: URL) -> [URL: Data]?{
                let data = try? url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
                if let data = data{
                    return [url: data]
                }
                return nil
            }
    
            private func getBookmarkURL() -> URL? {
                let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
                if let appSupportURL = urls.last{
                    let url = appSupportURL.appendingPathComponent("Bookmarks.dict")
                    return url
                }
                return nil
            }
    
            private func fileExists(_ url: URL) -> Bool
            {
                var isDir = ObjCBool(false)
                let exists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir)
    
                return exists
            }
    
        }
    
    0 讨论(0)
  • 2021-02-02 01:32

    Here is my answer that I've just got 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 must first call NSOpenPanel so the user can select 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 app will have the same level of access as it did when the user selected the folder. Hope this helps someone.

    0 讨论(0)
  • 2021-02-02 01:47

    security scoped bookmarks is exactly the way to go. good starting point is apple documentation on AppStore sandbox (which includes sample code) and class reference of NSFileManager.

    you then will not store the path in user defaults but the binary data of the bookmark.

    0 讨论(0)
提交回复
热议问题