Save/Copy a file from Bundle to Desktop using NSSavePanel

3.1k views Asked by At

I’m creating a macOS app which ships with some .zip files within its Bundle directory.

Users should be able to save these files from my app to a custom directory.

I found NSSavePanel and thought it is the right approach — that’s what I have so far:

@IBAction func buttonSaveFiles(_ sender: Any) {

    let savePanel = NSSavePanel()

    let bundleFile = Bundle.main.resourcePath!.appending("/MyCustom.zip")

    let targetPath = NSHomeDirectory()
    savePanel.directoryURL = URL(fileURLWithPath: targetPath.appending("/Desktop")) 
    // Is appeding 'Desktop' a good solution in terms of localisation?

    savePanel.message = "My custom message."
    savePanel.nameFieldStringValue = "MyFile"
    savePanel.showsHiddenFiles = false
    savePanel.showsTagField = false
    savePanel.canCreateDirectories = true
    savePanel.allowsOtherFileTypes = false
    savePanel.isExtensionHidden = true

    savePanel.beginSheetModal(for: self.view.window!, completionHandler: {_ in })

}

I couldn’t find out how to 'hand over' the bundleFile to the savePanel.

So my main question is: How can I save/copy a file from the app bundle to a custom directory?

Additional questions depending NSSavePanel: 1) It seems that it’s not localized by default (my Xcode scheme is set to German, but the panel appears in English), do I have to customize that by myself? 2) Is there a way to present the panel expanded by default?

1

There are 1 answers

3
Eric Aya On BEST ANSWER

You should use Bundle.main.url to get your existing file URL, then get the destination URL with the panel, then copy the file. The panel doesn't do anything to files, it just gets their URL.

Example:

// the panel is automatically displayed in the user's language if your project is localized
let savePanel = NSSavePanel()

let bundleFile = Bundle.main.url(forResource: "MyCustom", withExtension: "zip")!

// this is a preferred method to get the desktop URL
savePanel.directoryURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!

savePanel.message = "My custom message."
savePanel.nameFieldStringValue = "MyFile"
savePanel.showsHiddenFiles = false
savePanel.showsTagField = false
savePanel.canCreateDirectories = true
savePanel.allowsOtherFileTypes = false
savePanel.isExtensionHidden = true

if let url = savePanel.url, savePanel.runModal() == NSFileHandlingPanelOKButton {
    print("Now copying", bundleFile.path, "to", url.path)
    // Do the actual copy:
    do {
        try FileManager().copyItem(at: bundleFile, to: url)
    } catch {
        print(error.localizedDescription)
    }
} else {
    print("canceled")
}

Also, note that the panel being expanded or not is a user selection, you can't force it from your app.