macOS store sandbox应用程序使用NSOpenPanel选择下载文件文件夹,但无法再次访问该文件夹
我的应用程序是从网站下载文件 我为macOS商店启用了项目的沙箱 应用程序将触发NSOpenPanel,要求用户选择下载文件(所有文件列表存储在sqlite文件中)保存到的文件夹。 例如:macOS store sandbox应用程序使用NSOpenPanel选择下载文件文件夹,但无法再次访问该文件夹,macos,cocoa,sandbox,Macos,Cocoa,Sandbox,我的应用程序是从网站下载文件 我为macOS商店启用了项目的沙箱 应用程序将触发NSOpenPanel,要求用户选择下载文件(所有文件列表存储在sqlite文件中)保存到的文件夹。 例如: /home/mymac/myfolder 一切都好。如果我关闭应用程序并重新打开它,我希望它可以继续下载文件(在sqlite文件中) 但它报告了错误: 设置安全信息:不允许操作 系统似乎不允许应用程序访问该文件夹 /home/mymac/myfolder /home/mymac/Downloads 再
/home/mymac/myfolder
一切都好。如果我关闭应用程序并重新打开它,我希望它可以继续下载文件(在sqlite文件中)
但它报告了错误:
设置安全信息:不允许操作
系统似乎不允许应用程序访问该文件夹
/home/mymac/myfolder
/home/mymac/Downloads
再说一遍
如果我使用NSOpenPanel选择系统下载文件夹
/home/mymac/Downloads
关闭应用程序并重新打开应用程序,一切正常。
看起来系统只允许应用程序访问文件夹
/home/mymac/myfolder
/home/mymac/Downloads
再说一遍
欢迎您发表评论您可以为此使用安全书签。我已附上我正在使用的类:
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
所需的窗口实例。您可以在其中一个NSViewController的视图显示中设置实例:
override func viewDidAppear()
{
super.viewDidAppear()
SecureFolders.window = NSApplication.shared.mainWindow
}
您可以为此使用安全书签。我已附上我正在使用的类:
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
所需的窗口实例。您可以在其中一个NSViewController的视图显示中设置实例:
override func viewDidAppear()
{
super.viewDidAppear()
SecureFolders.window = NSApplication.shared.mainWindow
}
您需要获取URL的书签并将其永久存储。当应用程序打开时,从存储的书签中检索URL
操作方法在文档中有描述:
您只需要两种方法:
- (NSData*)bookmarkForURL:(NSURL*)url
- (NSURL*)urlForBookmark:(NSData*)bookmark
您可以将书签存储在.plist
文件中,如果您不希望有大量书签,甚至可以存储在UserDefaults中。您需要获取URL的书签并将其永久存储。当应用程序打开时,从存储的书签中检索URL
操作方法在文档中有描述:
您只需要两种方法:
- (NSData*)bookmarkForURL:(NSURL*)url
- (NSURL*)urlForBookmark:(NSData*)bookmark
您可以将书签存储在.plist
文件中,如果您不希望有很多书签,甚至可以存储在UserDefaults中。谢谢,我的问题不是url,而是下载文件夹无法重新打开或访问是的,我明白了。无论您从NSOpenPanel获得什么,都需要将其存储为书签,以便在下次发布时访问。它可能是下载文件夹,不管怎样。另一种方法是将您需要的文件存储在应用程序的目录中。你总是可以访问它。例如:let downloads=FileManager.default.url(用于:.downloadsDirectory,in:.localDomainMask)。首先
app的目录不能存储用户文件,苹果拒绝了我的应用程序,我使用NSString存储文件夹,我也尝试NSURL NSData存储信息,没有任何更改,重新启动应用程序并访问同一文件夹时出现相同错误“无法在第42249行打开文件os_unix.c:42249:(0)-未定义错误:0”谢谢,我的问题不是url,而是下载文件夹无法重新打开或访问是的,我明白了。无论您从NSOpenPanel获得什么,都需要将其存储为书签,以便在下次发布时访问。它可能是下载文件夹,不管怎样。另一种方法是将您需要的文件存储在应用程序的目录中。你总是可以访问它。例如:let downloads=FileManager.default.url(用于:.downloadsDirectory,in:.localDomainMask)。首先
app的目录不能存储用户文件,苹果拒绝了我的应用程序,我使用NSString存储文件夹,我也尝试NSURL NSData存储信息,没有任何更改,重新启动应用程序并访问同一文件夹时出现相同错误“无法在第42249行打开文件os_unix.c:42249:(0)-未定义错误:0”谢谢,我的问题不是url,而是下载文件夹无法重新打开或访问Hanks,我的问题不是url,而是下载文件夹无法重新打开或访问