swift 获取在macOS上写入多个文件的权限

nwlls2ji  于 2023-03-28  发布在  Swift
关注(0)|答案(1)|浏览(222)

我有一个Swift程序可以帮助组织猫展。它会生成几个rtf文件和一个xml文件,这些文件应该保存在磁盘上的一个文件夹中,并且只需要对它们的文件名进行微小的修改-第一个可以命名为“Cat show. rtf”,然后是“Cat show judges. rtf”,然后是“Cat show. xml”。
我想只显示一个NSSavePanel来定位保存文件的文件夹,而不是为每个文件不断弹出一个保存面板。
我尝试使用NSSavePanel获取第一个文件的URL,然后修改每个后续文件的URL。第一个文件已保存,但后面的每个文件都出现“不允许操作”错误,因此未保存。我尝试使用NSSavePanel获取用于保存文件的文件夹,但是所有文件都出现了“不允许操作”的错误。我可以使用nsavepanel单独获取每个文件的URL,但我不想这样做,因为这会让用户感到困惑。
有人知道我该怎么做吗?
我用来修改URL的代码是:

func newURL(from oldURL : URL, with newName: String) -> URL {
  return oldURL.deletingLastPathComponent().appendingPathComponent(newName)
}

let challengesURL  = newURL(from: url, with: "\(name).xml")
euoag5mw

euoag5mw1#

好的,我已经确认了这里发生的事情。首先,你需要启用AppSandbox,并允许对用户选择的文件进行读/写。我已经在下面的评论中注意到了如何做到这一点,但由于你能够保存用户选择的文件名,我建议这是可以的。
简而言之,NSSavePanel只返回输入文件名的安全作用域URL,而不是目录。因此,您可以多次保存该文件,但不能调整文件名(因为它是一个没有权限的新URL)。一个可能的解决方法和我以前如何保存多个文件是使用一个NSOpenPanel,它允许用户选择一个目录。这将返回一个安全作用域的目录url,因此您有权访问该作用域中的目录。这允许您在该目录中保存多个文件。
在下面的代码中,我放了两个解决方案来演示这一点,你可以看到NSSavePanel,它保存了两次输入的文件名和修改的内容,但没有写入编程修改的文件名。通过使用NSOpenPanel选择一个目录,并从用户收集文件名在一个文本字段中,所有文件都保存得很好。
希望这个有用。

struct ContentView: View {
    @State var sampleData: [String] = ["Rob" , "Jane" , "Freddy" , "Peter"]
    @State var filename: String = ""
    
    var body: some View {
        VStack {
            Text("File Tests")
            TextField("Enter Filename:", text: $filename).frame(width: 200)
            Button("Save Using Save Panel") {
                savePanel()
            }
            Button("Save NSOpenPanel Directory") {
                savePanelUsingOpenPanelv2()
            }
        }
    }
    
    func savePanelUsingOpenPanelv2() {
        let panel = NSOpenPanel()
        // Sets up so user can only select a single directory
        panel.canChooseFiles = false
        panel.canChooseDirectories = true
        panel.allowsMultipleSelection = false
        panel.showsHiddenFiles = false
        panel.title = "Select Save Directory"
        panel.prompt = "Select Save Directory"
        
        let response = panel.runModal()
        if response == .OK {
            if let panelURL = panel.url { // This is a directory url
                let saveURL = panelURL.appending(component: "\(filename).txt")
                self.saveListAsURL(saveURL) // Saving the initial filename
                print(saveURL.description)
                let newName = "NewNameDirectoryTest"
                let updatedURL = newURL(from: saveURL, with: "\(newName).txt") // Updating the url
                // Updating the array saved
                sampleData.append("Joe")
                sampleData.append("Jack")
                // Saving the updated URL / filename.
                saveListAsURL(updatedURL) // Work fine as directory has security scope.
                print(updatedURL.description)
            }
        }
    }
    
    func savePanel() {
        let savePanel = NSSavePanel()
        if savePanel.runModal() == .OK {
            if let url = savePanel.url { // This is a file url
                self.saveListAsURL(url) // Saving with the panel selected filename
                print(url.description)
                let newName = "NewNameSavePanelTest"
                let updatedURL = newURL(from: url, with: "\(newName).txt")
                
                sampleData.append("Joe")
                sampleData.append("Jack")

                saveListAsURL(updatedURL) // Saving updated url fails
                print(updatedURL.description)
                saveListAsURL(url) // saving original url again works fine.
                print(url.description)
            }
        }
    }
    
    func newURL(from oldURL : URL, with newName: String) -> URL {
      return oldURL.deletingLastPathComponent().appendingPathComponent(newName)
    }
    
    func saveListAsURL(_ url: URL) {
        let fm = FileManager()
        var saveStringArray: [String] = []
        var data = Data()
        
        for name in sampleData {
            let string = name + "\n"
            saveStringArray.append(string)
            let stringData = string.utf8
            data.append(contentsOf: stringData)
        }
        
        let saveResult = fm.createFile(atPath: url.path  , contents: data)
        print("Save results is \(saveResult)")
    }
    
}

NSSavePanel和NSOpenPanel文档确认了返回URL的安全范围。有一些功能可以保存安全范围的NSURL书签,但我以前没有使用过。这里的文档中有关于书签的详细信息。这似乎是一个额外的复杂程度。
https://developer.apple.com/documentation/foundation/nsurl
您可以考虑将应用程序授权添加到特定目录,但由于您似乎希望用户选择他们想要保存的任何地方,因此Panel方法是一种可行的方法,因为不建议使用完全开放的授权。
https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html#//apple_ref/doc/uid/TP40011195-CH5-SW7

相关问题