macOS 商店沙盒应用程序使用 NSOpenPanel select 下载文件夹,但无法再次访问该文件夹
macOS store sandbox app uses NSOpenPanel to select download file folder, but can not access the folder again
我的应用是从网站下载文件。
我为 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
所需的 window 实例。您可以在其中一个 NSViewControllers 的 viewDidAppear
中设置实例:
override func viewDidAppear()
{
super.viewDidAppear()
SecureFolders.window = NSApplication.shared.mainWindow
}
您需要获取 URL 的书签并永久保存。当您的应用程序打开时,从存储的书签中检索 URL。
文档中描述了执行此操作的方法:Locating Files Using Bookmarks
你只需要2个方法:
- (NSData*)bookmarkForURL:(NSURL*)url
- (NSURL*)urlForBookmark:(NSData*)bookmark
您可以将书签存储在 .plist
文件中,如果您不希望有很多书签,甚至可以存储在 UserDefaults 中。
我的应用是从网站下载文件。
我为 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
所需的 window 实例。您可以在其中一个 NSViewControllers 的 viewDidAppear
中设置实例:
override func viewDidAppear()
{
super.viewDidAppear()
SecureFolders.window = NSApplication.shared.mainWindow
}
您需要获取 URL 的书签并永久保存。当您的应用程序打开时,从存储的书签中检索 URL。
文档中描述了执行此操作的方法:Locating Files Using Bookmarks
你只需要2个方法:
- (NSData*)bookmarkForURL:(NSURL*)url
- (NSURL*)urlForBookmark:(NSData*)bookmark
您可以将书签存储在 .plist
文件中,如果您不希望有很多书签,甚至可以存储在 UserDefaults 中。