【问题标题】:File access in a sandboxed Mac app with swift使用 swift 在沙盒 Mac 应用程序中访问文件
【发布时间】:2015-09-25 13:10:40
【问题描述】:

我正在开发一个适用于 OS X 10.9 的应用程序,带有 swift、沙盒。

应用需要访问 SQLite 数据库文件。我让用户使用 NSOpenPanel 选择/打开一个文件。然后我用 NSUserDefaults 保存文件路径供以后使用。

我希望每次重新启动应用程序时自动打开此文件。我从 NSUserDefault 获取存储的路径,但是当我使用此路径打开文件时出现错误,说我无权访问该文件。

(它在没有沙盒的情况下工作)

看来书签可以解决我的问题。

是否有一个很好的教程如何在 osx 应用程序中快速使用书签? 还有什么建议吗?

【问题讨论】:

    标签: swift macos nsurl nsfilemanager appstore-sandbox


    【解决方案1】:

    这是我在http://swiftrien.blogspot.com/2015/07/persisting-file-access-rights-between.html 的帮助下刚刚在 Swift 3 中工作的答案

    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(更新):

    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
    }
    

    要使用此代码,您必须首先调用 NSOpenPanel,以便用户可以选择授予您访问权限的文件夹。 NSOpenPanel 必须存储为书签并保存到磁盘。

    let url = allowFolder()
    saveBookmarks()
    

    当您重新启动应用程序时,您必须调用

    loadBookmarks()
    

    那么您的应用将拥有与用户选择文件夹时相同的访问级别。希望这对某人有所帮助。

    【讨论】:

    • 如果isStale == true,看来你得打电话给url.startAccessingSecurityScopedResource。否则将无法正常工作。
    【解决方案2】:

    这里有更简洁的 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
            }
    
        }
    

    【讨论】:

      【解决方案3】:

      安全范围的书签正是要走的路。好的起点是 AppStore 沙箱上的苹果文档(包括示例代码)和 NSFileManager 的类参考。

      然后您将不会将路径存储在用户默认值中,而是将书签的二进制数据存储。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-11
        相关资源
        最近更新 更多