繁体   English   中英

macOS 商店沙盒应用使用 NSOpenPanel 到 select 下载文件夹,但无法再次访问该文件夹

[英]macOS store sandbox app uses NSOpenPanel to select download file folder, but can not access the folder again

我的应用程序是从 web 站点下载文件。

我为 macOS 商店启用了项目的沙箱。

在此处输入图像描述

该应用程序将触发 NSOpenPanel 询问用户 select 下载文件(所有文件列表存储在 sqlite 文件中)的文件夹。 例如:

/home/mymac/myfolder

一切都好。 如果我关闭应用程序并重新打开它,我希望它可以继续下载文件(在 sqlite 文件中)。

但是报错:设置安全信息:不允许操作

看起来系统不允许应用程序访问该文件夹

/home/mymac/myfolder

再次。

如果我使用 NSOpenPanel 到 select 系统下载文件夹

/home/mymac/Downloads

关闭应用程序并重新打开应用程序,一切正常。 看起来系统只允许应用访问文件夹

/home/mymac/Downloads

再次。

欢迎您的评论

您可以为此使用安全书签。 我附上了我正在使用的 class:

import Foundation
import Cocoa

public class SecureFolders
{
    public static var window: NSWindow?

    private static var folders = [URL : Data]()
    private static var path: String?

    public static func initialize(_ path: String)
    {
        self.path = path
    }

    public static func load()
    {
        guard let path = self.path else { return }

        if !FileManager.default.fileExists(atPath: path)
        {
            return
        }

        if let rawData = NSData(contentsOfFile: path)
        {
            let data = Data(referencing: rawData)

            if let folders = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [URL : Data]
            {
                for folder in folders
                {
                    self.restore(folder)
                }
            }
        }
    }

    public static func remove(_ url: URL)
    {
        folders.removeValue(forKey: url)
    }

    public static func store(url: URL)
    {
        guard let path = self.path else { return }

        do
        {
            let data = try NSKeyedArchiver.archivedData(withRootObject: self.folders, requiringSecureCoding: false)
            self.folders[url] = data

            if let url = URL(string: path)
            {
                try? data.write(to: url)
            }
        }
        catch
        {
            Swift.print("Error storing bookmarks")
        }
    }

    public static func restore(_ folder: (key: URL, value: Data))
    {
        let restoredUrl: URL?
        var isStale = false

        do
        {
            restoredUrl = try URL.init(resolvingBookmarkData: folder.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)")
                }

                self.folders[url] = folder.value
            }
        }
    }

    public static func allow(folder: String, prompt: String, callback: @escaping (URL?) -> ())
    {
        let openPanel = NSOpenPanel()
        openPanel.directoryURL = URL(string: folder)
        openPanel.allowsMultipleSelection = false
        openPanel.canChooseDirectories = true
        openPanel.canCreateDirectories = false
        openPanel.canChooseFiles = false
        openPanel.prompt = prompt

        openPanel.beginSheetModal(for: self.window!)
        {
            result in

            if result == NSApplication.ModalResponse.OK
            {
                let url = openPanel.url
                self.store(url: url!)

                callback(url)
            }
            else
            {
                callback(nil)
            }
        }
    }

    public static func isStored(_ directory: Directory) -> Bool
    {
        return isStored(path: IO.getDirectory(directory))
    }

    public static func remove(_ directory: Directory)
    {
        let path = IO.getDirectory(directory)
        self.remove(path)
    }

    public static func remove(_ path: String)
    {
        let url = URL(fileURLWithPath: path)
        self.remove(url)
    }

    public static func isStored(path: String) -> Bool
    {
        let absolutePath = URL(fileURLWithPath: path).path

        for url in self.folders
        {
            if url.key.path == absolutePath
            {
                return true
            }
        }

        return false
    }

    public static func areStored(_ directories: [Directory]) -> Bool
    {
        for dir in directories
        {
            if isStored(dir) == false
            {
                return false
            }
        }

        return true
    }

    public static func areStored(_ paths: [String]) -> Bool
    {
        for path in paths
        {
            if isStored(path: path) == false
            {
                return false
            }
        }

        return true
    }
}

用法:

fileprivate func initialize() // Put a call to this in func applicationDidFinishLaunching(_ aNotification: Notification)
{
    let directories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let path = directories[0].appending("/SecureBookmarks.dict")

    SecureFolders.initialize(path)
    SecureFolders.load()
}

要将文件夹添加到安全书签:

fileprivate func allow(_ path: String)
{
    SecureFolders.allow(folder: path, prompt: "Open")
    {
        result in
        // Update controls or whatever
    }
}

不要忘记设置显示 NSOpenPanel 所需的NSOpenPanel实例。 您可以在您的 NSViewController 之一的viewDidAppear中设置实例:

override func viewDidAppear()
{
    super.viewDidAppear()
    SecureFolders.window = NSApplication.shared.mainWindow
}

您需要获取 URL 的书签并将其永久存储。 当您的应用程序打开时,从存储的书签中检索 URL。

文档中描述了这样做的方法: Locating Files Using Bookmarks

您只需要两种方法:

- (NSData*)bookmarkForURL:(NSURL*)url
- (NSURL*)urlForBookmark:(NSData*)bookmark

如果您不希望有很多书签,您可以将书签存储在.plist文件中,甚至可以存储在 UserDefaults 中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM