Repository: utmapp/UTM
File: Platform/macOS/SavePanel.swift (Swift)

Code before:
    var shareItem: Any

    func makeNSView(context: Context) -> some NSView {
        return NSView()
    }

    func updateNSView(_ nsView: NSViewType, context: Context) {
        if isPresented {
            let savePanel = NSSavePanel()
            savePanel.directoryURL = URL(fileURLWithPath: "/Users/\(NSUserName())/Downloads")

            if let sourceUrl = shareItem as? URL {
                if sourceUrl.pathExtension == "log" {
                    savePanel.title = "Select where to save debug log:"
                    savePanel.nameFieldStringValue = "debug"
                    savePanel.allowedContentTypes = [.appleLog]
                } else if sourceUrl.pathExtension == "utm" {
                    savePanel.title = "Select where to save UTM Virtual Machine:"
                    savePanel.nameFieldStringValue = sourceUrl.deletingPathExtension().lastPathComponent
                    savePanel.allowedContentTypes = [.UTM]
                }

                savePanel.begin { result in
                    if result == .OK {
                        if let destUrl = savePanel.url {
                            try? FileManager.default.copyItem(at: sourceUrl, to: destUrl)
                        }
                    } else {
                        NSSound.beep()
                    }
                }
            } else if let command = shareItem as? String {
                savePanel.title = "Select where to export QEMU command:"
                savePanel.nameFieldStringValue = "command"
                savePanel.allowedContentTypes = [.plainText]

                savePanel.begin { result in
                    if result == .OK {
                        if let destUrl = savePanel.url {
                            try? command.write(to: destUrl, atomically: true, encoding: .utf8)
                        }
                    } else {
                        NSSound.beep()
                    }
                }
            }

            DispatchQueue.main.async {
                isPresented = false
            }
        }

Review comment (suggestion):
Is an error available here? Errors can be shown in UTM by setting `UTMData.alertMessage`. (available as `@EnvironmentObject`)

Code after:
    @Binding var isPresented: Bool
    var shareItem: VMShareItemModifier.ShareItem?

    func makeNSView(context: Context) -> some NSView {
        return NSView()
    }

    func updateNSView(_ nsView: NSViewType, context: Context) {
        if isPresented {
            guard let shareItem = shareItem else {
                return
            }
            
            // Initializing the SavePanel and setting it's properties
            let savePanel = NSSavePanel()
            savePanel.directoryURL = URL(fileURLWithPath: "/Users/\(NSUserName())/Downloads")
            
            switch shareItem {
            case .debugLog:
                savePanel.title = "Select where to save debug log:"
                savePanel.nameFieldStringValue = "debug"
                savePanel.allowedContentTypes = [.appleLog]
            case let .utmVm(sourceUrl):
                savePanel.title = "Select where to save UTM Virtual Machine:"
                savePanel.nameFieldStringValue = sourceUrl.deletingPathExtension().lastPathComponent
                savePanel.allowedContentTypes = [.UTM]
            case .qemuCommand:
                savePanel.title = "Select where to export QEMU command:"
                savePanel.nameFieldStringValue = "command"
                savePanel.allowedContentTypes = [.plainText]
            }
            
            // Calling savePanel.begin with the appropriate completion handlers
            switch shareItem {
            case .debugLog(let sourceUrl), .utmVm(let sourceUrl):
                savePanel.begin { result in
                    if result == .OK {
                        if let destUrl = savePanel.url {
                            do {
                                let fileManager = FileManager.default
                                
                                // All this mess is because FileManager.replaceItemAt deletes the source item
                                let tempUrl = fileManager.temporaryDirectory.appendingPathComponent(sourceUrl.lastPathComponent)
                                if fileManager.fileExists(atPath: tempUrl.path) {
                                    try fileManager.removeItem(at: tempUrl)
                                }
                                try fileManager.copyItem(at: sourceUrl, to: tempUrl)
                                
                                _ = try fileManager.replaceItemAt(destUrl, withItemAt: tempUrl)
                            } catch {
                                data.alertMessage = AlertMessage(error.localizedDescription)