# UTM Virtualization Platform UTM is a full-featured system emulator and virtual machine host for iOS and macOS, built on QEMU. It enables users to run Windows, Linux, and other operating systems on Apple devices through both QEMU emulation and Apple's native Virtualization framework. The platform provides 30+ processor architectures including x86_64, ARM64, and RISC-V, with support for VGA graphics via SPICE/QXL, USB devices, and JIT-based acceleration. The architecture is designed with protocol-based abstraction, separating backend implementations (QEMU vs Apple Virtualization) from the UI layer. This enables seamless support for multiple virtualization engines while maintaining a consistent API. UTM includes remote VDI capabilities for networked access to VMs, a CLI tool (utmctl) for automation, AppleScript integration for macOS workflows, and comprehensive configuration management for drives, networks, displays, and peripherals. ## Virtual Machine Lifecycle Management ### Creating and starting a VM Create a new virtual machine from configuration and manage its lifecycle through async/await operations. ```swift import Foundation // Create VM from configuration let config = UTMQemuConfiguration() config.information.name = "Ubuntu Server" config.system.architecture = .x86_64 config.system.target = QEMUTarget_x86_64.q35 config.system.memorySize = 4096 // MB config.system.cpuCount = 4 let packageUrl = URL(fileURLWithPath: "/Users/username/Virtual Machines/Ubuntu.utm") let vm = try UTMQemuVirtualMachine( packageUrl: packageUrl, configuration: config, isShortcut: false ) // Save to disk try await vm.save() // Start the VM do { try await vm.start(options: []) print("VM started successfully") print("Current state: \(vm.state)") // .started } catch { print("Failed to start: \(error)") } // Stop the VM gracefully try await vm.stop(usingMethod: .request) // Force stop if needed try await vm.stop(usingMethod: .force) // Kill the process immediately try await vm.stop(usingMethod: .kill) ``` ### Snapshot management Save, restore, and delete VM snapshots for quick rollback capabilities. ```swift // Save current state as snapshot try await vm.saveSnapshot(name: "clean-install") print("Snapshot saved") // Continue working with VM... try await vm.start(options: []) // Restore to saved snapshot try await vm.restoreSnapshot(name: "clean-install") print("Restored to snapshot") // Delete snapshot when no longer needed try await vm.deleteSnapshot(name: "clean-install") // Save default snapshot (unnamed) try await vm.saveSnapshot(name: nil) // Check if snapshots are supported if vm.snapshotUnsupportedError != nil { print("Snapshots not supported for this VM") } ``` ### Pause and resume operations Temporarily pause VM execution without stopping the virtual machine. ```swift // Pause VM try await vm.pause() print("VM paused, state: \(vm.state)") // .paused // Resume execution try await vm.resume() print("VM resumed, state: \(vm.state)") // .started // Restart VM (stop and start) try await vm.restart() ``` ## Configuration System ### QEMU configuration with drives Configure a complete QEMU-based virtual machine with multiple drives and devices. ```swift let config = UTMQemuConfiguration() // Basic system settings config.information.name = "Windows 10" config.information.notes = "Development environment" config.system.architecture = .x86_64 config.system.target = QEMUTarget_x86_64.q35 config.system.cpu = QEMUCPU_x86_64.host config.system.memorySize = 8192 // 8GB config.system.cpuCount = 0 // Auto-detect // Create main disk drive var mainDrive = UTMQemuConfigurationDrive() mainDrive.imageName = "windows.qcow2" mainDrive.imageType = .disk mainDrive.interface = .nvme mainDrive.sizeMib = 102400 // 100GB mainDrive.isReadOnly = false config.drives.append(mainDrive) // Add CD-ROM drive for installation var cdDrive = UTMQemuConfigurationDrive() cdDrive.imageName = "windows-install.iso" cdDrive.imageType = .cd cdDrive.interface = .ide cdDrive.isReadOnly = true config.drives.append(cdDrive) // Add external drive (bookmark-based) var externalDrive = UTMQemuConfigurationDrive() externalDrive.imageType = .disk externalDrive.interface = .usb externalDrive.isExternal = true externalDrive.imageURL = URL(fileURLWithPath: "/path/to/external.img") config.drives.append(externalDrive) // Configure network var network = UTMQemuConfigurationNetwork() network.mode = .emulated network.hardware = QEMUNetworkDevice_x86_64.virtio network.macAddress = "52:54:00:12:34:56" // Port forwarding for SSH var sshForward = UTMQemuConfigurationPortForward() sshForward.protocol = .tcp sshForward.guestPort = 22 sshForward.hostPort = 2222 network.portForward = [sshForward] config.networks = [network] // Display configuration var display = UTMQemuConfigurationDisplay() display.upscaler = .linear display.downscaler = .linear config.displays = [display] // Save configuration let vm = try UTMQemuVirtualMachine( packageUrl: URL(fileURLWithPath: "/path/to/Windows10.utm"), configuration: config, isShortcut: false ) try await vm.save() ``` ### Apple Virtualization framework configuration Configure a VM using Apple's native Virtualization framework for macOS guests. ```swift @available(macOS 12, *) func createMacOSVM() throws { let config = UTMAppleConfiguration() // Basic information config.information.name = "macOS Ventura" config.system.memorySize = 8192 // 8GB config.system.cpuCount = 4 // Boot configuration config.boot.operatingSystem = .macOS config.boot.macRecoveryIpswURL = URL( string: "https://updates.cdn-apple.com/..." ) // Main drive var drive = UTMAppleConfigurationDrive() drive.imageName = "macos.img" drive.sizeMib = 102400 // 100GB drive.isReadOnly = false config.drives = [drive] // Network var network = UTMAppleConfigurationNetwork() network.mode = .nat config.networks = [network] // Display var display = UTMAppleConfigurationDisplay() display.width = 1920 display.height = 1080 display.pixelsPerInch = 226 config.displays = [display] // Shared directory (macOS host to guest) var sharedDir = UTMAppleConfigurationSharedDirectory() sharedDir.directoryURL = URL(fileURLWithPath: "/Users/username/Shared") sharedDir.isReadOnly = false config.sharedDirectories = [sharedDir] let vm = try UTMAppleVirtualMachine( packageUrl: URL(fileURLWithPath: "/path/to/macOS.utm"), configuration: config, isShortcut: false ) try await vm.save() // Start in recovery mode for installation try await vm.start(options: [.bootRecovery]) } ``` ### Loading and modifying existing configuration Load an existing VM configuration, modify settings, and save changes. ```swift // Load existing VM let vmUrl = URL(fileURLWithPath: "/path/to/MyVM.utm") let config = try UTMConfiguration.load(from: vmUrl) if var qemuConfig = config as? UTMQemuConfiguration { // Modify memory qemuConfig.system.memorySize = 16384 // 16GB // Add new network interface var newNetwork = UTMQemuConfigurationNetwork() newNetwork.mode = .bridged newNetwork.hardware = QEMUNetworkDevice_x86_64.virtio qemuConfig.networks.append(newNetwork) // Add serial port for console access var serial = UTMQemuConfigurationSerial() serial.mode = .tcpServer serial.tcpHostAddress = "0.0.0.0" serial.tcpPort = 4555 qemuConfig.serials = [serial] // Create VM with modified config let vm = try UTMQemuVirtualMachine( packageUrl: vmUrl, configuration: qemuConfig, isShortcut: false ) try await vm.save() } ``` ## State Management with VMData ### Observable VM wrapper for SwiftUI Wrap a VM for reactive SwiftUI integration with published properties. ```swift import SwiftUI import Combine @MainActor class MyVMManager: ObservableObject { @Published var vmData: VMData? @Published var errorMessage: String? func loadVM(at url: URL) { do { vmData = try VMData(url: url) // Observe state changes vmData?.$state.sink { [weak self] state in print("VM state changed to: \(state)") if state == .started { self?.onVMStarted() } }.store(in: &cancellables) // Observe screenshot updates vmData?.$screenshot.sink { screenshot in if let image = screenshot?.image { print("Screenshot updated") } }.store(in: &cancellables) } catch { errorMessage = error.localizedDescription } } func startVM() { vmData?.requestVmStart(options: []) } func stopVM() { vmData?.requestVmStop(force: false) } func pauseAndSave() { vmData?.requestVmPause(save: true) } private func onVMStarted() { print("VM is now running") } private var cancellables = Set() } // SwiftUI View struct VMControlView: View { @StateObject var manager = MyVMManager() var body: some View { VStack { if let vm = manager.vmData { Text(vm.detailsTitleLabel) .font(.title) Text("State: \(vm.state.rawValue)") if let screenshot = vm.screenshot?.image { Image(nsImage: screenshot) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 200) } HStack { Button("Start") { manager.startVM() } .disabled(vm.state != .stopped) Button("Stop") { manager.stopVM() } .disabled(vm.state == .stopped) Button("Pause") { manager.pauseAndSave() } .disabled(vm.state != .started) } } if let error = manager.errorMessage { Text("Error: \(error)") .foregroundColor(.red) } } .padding() .onAppear { manager.loadVM(at: URL(fileURLWithPath: "/path/to/VM.utm")) } } } ``` ### Managing multiple VMs with UTMData Centralized management of multiple virtual machines with the main orchestrator. ```swift @MainActor class VMListManager: ObservableObject { let data = UTMData() @Published var allVMs: [VMData] = [] @Published var runningVMs: [VMData] = [] func loadAllVMs() async { // Load VMs from default directory await data.loadVirtualMachines(from: data.defaultStorageUrl) allVMs = data.virtualMachines updateRunningList() } func createNewVM(name: String, os: String) async throws { let config = UTMQemuConfiguration() config.information.name = name let destinationUrl = data.defaultStorageUrl .appendingPathComponent("\(name).utm") let vmData = try VMData( creatingFromConfig: config, destinationUrl: destinationUrl ) // Add to list data.virtualMachines.append(vmData) try await data.save(vm: vmData) allVMs = data.virtualMachines } func deleteVM(_ vm: VMData) async throws { await data.delete(vm: vm) allVMs = data.virtualMachines } func startAllVMs() async { for vm in allVMs where vm.state == .stopped { vm.requestVmStart() try? await Task.sleep(nanoseconds: 2_000_000_000) // 2s delay } updateRunningList() } func stopAllVMs() async { for vm in runningVMs { vm.requestVmStop(force: false) } try? await Task.sleep(nanoseconds: 1_000_000_000) updateRunningList() } private func updateRunningList() { runningVMs = allVMs.filter { $0.state == .started || $0.state == .starting } } } ``` ## Registry and Persistent Metadata ### Working with registry entries Access and modify per-VM runtime metadata stored in the registry. ```swift // Access registry entry for a VM let entry = vm.registryEntry // Update VM name in registry entry.name = "New VM Name" // Add external drive bookmark let driveUrl = URL(fileURLWithPath: "/path/to/external.img") let driveId = "drive-001" entry.externalDrives[driveId] = UTMRegistryEntry.File( path: driveUrl.path, bookmark: try? driveUrl.bookmarkData( options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil ), isValid: true ) // Add shared directory let sharedUrl = URL(fileURLWithPath: "/Users/username/Shared") entry.sharedDirectories.append(UTMRegistryEntry.File( path: sharedUrl.path, bookmark: try? sharedUrl.bookmarkData( options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil ), isValid: true )) // Set window size for display 0 entry.windowSettings[0] = UTMRegistryEntry.Window( displayIndex: 0, width: 1920, height: 1080 ) // Mark VM as suspended entry.isSuspended = true // Registry automatically saves to UserDefaults with debouncing // Force immediate save on app termination UTMRegistry.shared.sync() ``` ### Security-scoped bookmark management Handle file access outside the sandbox using security-scoped bookmarks. ```swift func addExternalResource(url: URL, to vm: any UTMVirtualMachine) throws { // Create security-scoped bookmark let bookmark = try url.bookmarkData( options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil ) // Store in registry let entry = vm.registryEntry let resourceId = UUID().uuidString entry.externalDrives[resourceId] = UTMRegistryEntry.File( path: url.path, bookmark: bookmark, isValid: true ) print("Added external resource: \(url.lastPathComponent)") } func accessExternalResource(fileEntry: UTMRegistryEntry.File) throws -> URL { guard let bookmarkData = fileEntry.bookmark else { throw NSError(domain: "UTM", code: 1, userInfo: [NSLocalizedDescriptionKey: "No bookmark data"]) } var isStale = false let url = try URL( resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale ) if isStale { print("Warning: Bookmark is stale, consider recreating") } // Start accessing the resource guard url.startAccessingSecurityScopedResource() else { throw NSError(domain: "UTM", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot access resource"]) } defer { url.stopAccessingSecurityScopedResource() } // Use the URL... return url } ``` ## Remote VDI Server ### Starting and managing remote server Enable remote access to VMs over the network with encryption and authentication. ```swift import Foundation actor RemoteServerManager { let data: UTMData let server: UTMRemoteServer init(data: UTMData) { self.data = data self.server = UTMRemoteServer(data: data) } func startServer(port: Int = 9876, password: String? = nil) async throws { // Configure server settings UserDefaults.standard.set(port, forKey: "ServerPort") UserDefaults.standard.set(password != nil, forKey: "ServerPasswordRequired") if let password = password { // Store password securely (use Keychain in production) UserDefaults.standard.set(password, forKey: "ServerPassword") } UserDefaults.standard.set(true, forKey: "ServerExternal") // Start listening try await server.start() print("Remote server started on port \(port)") print("Server is discoverable via Bonjour as _utm_server._tcp") } func stopServer() async { await server.stop() print("Remote server stopped") } func listConnectedClients() async -> [String] { // Access connected client fingerprints // Implementation depends on server state exposure return [] } func resetServerKeys() async { await server.resetServer() print("Server keys regenerated, clients must reconnect") } } // Usage let manager = RemoteServerManager(data: utmData) try await manager.startServer(port: 9876, password: "securepass123") // Server now publishes VMs over mDNS // Clients can discover and connect via UTMRemoteClient ``` ### Remote client connection Connect to a remote UTM server and access VMs over the network. ```swift actor RemoteClientManager { let client: UTMRemoteClient init() { let state = UTMRemoteClient.State() self.client = UTMRemoteClient(state: state) } func discoverServers() async { await client.startScanning() print("Scanning for UTM servers...") // Servers will appear in client.state.discoveredServers try? await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds await client.stopScanning() } func connectToServer( host: String, port: Int, password: String?, fingerprint: Data ) async throws { let savedServer = UTMRemoteClient.State.SavedServer( name: "My UTM Server", host: host, port: port, fingerprint: fingerprint, requiresAuthentication: password != nil ) try await client.connect(savedServer) print("Connected to server at \(host):\(port)") // Now can access remote VMs through client.state.remoteVMs } func listRemoteVMs() async -> [String] { // Access remote VM list from state // Implementation depends on state exposure return [] } } // Usage let clientManager = RemoteClientManager() await clientManager.discoverServers() try await clientManager.connectToServer( host: "192.168.1.100", port: 9876, password: "securepass123", fingerprint: serverFingerprint ) ``` ## Command-Line Interface (utmctl) ### Basic VM operations via CLI Control VMs from the command line using the utmctl tool. ```bash # List all registered VMs utmctl list # Output: # UUID Status Name # 12345678-1234-1234-1234-123456789abc stopped Ubuntu Server # 87654321-4321-4321-4321-cba987654321 started Windows 10 # Start a VM by name utmctl start "Ubuntu Server" # Start by UUID utmctl start 12345678-1234-1234-1234-123456789abc # Check VM status utmctl status "Ubuntu Server" # Output: started # Suspend (pause) a VM utmctl suspend "Ubuntu Server" # Stop a VM gracefully utmctl stop "Ubuntu Server" # Force stop utmctl stop --force "Ubuntu Server" # Get VM version and info utmctl version ``` ### Advanced utmctl operations Execute commands in guest VMs and manage USB devices via CLI. ```bash # Execute command in guest (requires QEMU guest agent) utmctl exec "Ubuntu Server" -- ls -la /home utmctl exec "Ubuntu Server" -- systemctl status nginx # Get IP address of VM utmctl ip-address "Ubuntu Server" # Output: 10.0.2.15 # File operations utmctl file get "Ubuntu Server" /path/in/guest/file.txt ./local-file.txt utmctl file put "Ubuntu Server" ./local-file.txt /path/in/guest/file.txt # Attach to VM serial console (interactive) utmctl attach "Ubuntu Server" # Clone a VM utmctl clone "Ubuntu Server" "Ubuntu Clone" # Delete a VM utmctl delete "Ubuntu Server" # USB device management utmctl usb list utmctl usb attach "Windows 10" --device "USB Mass Storage" utmctl usb detach "Windows 10" --device "USB Mass Storage" # Run hidden (no GUI windows) utmctl --hide start "Ubuntu Server" ``` ## AppleScript Integration ### Automating VMs with AppleScript Control UTM virtual machines using AppleScript for macOS automation workflows. ```applescript -- Get UTM application tell application "UTM" -- List all VMs set vmList to every virtual machine repeat with vm in vmList log name of vm log status of vm end repeat -- Start a specific VM set myVM to first virtual machine whose name is "Ubuntu Server" start myVM -- Wait for VM to be running repeat while status of myVM is not started delay 1 end repeat log "VM is running" -- Get VM properties log "Backend: " & backend of myVM log "Architecture: " & architecture of myVM log "Memory: " & memory of myVM -- Stop the VM suspend myVM -- pause delay 2 stop myVM end tell ``` ### Creating VMs via AppleScript Programmatically create and configure new virtual machines. ```applescript tell application "UTM" -- Create new QEMU VM set newVM to make new virtual machine with properties {¬ name:"Debian Testing", ¬ backend:qemu, ¬ architecture:"x86_64", ¬ memory:"4096"} -- Configure network (if API supports) -- Note: Full configuration may require file manipulation -- Start the new VM start newVM log "Created and started: " & name of newVM end tell ``` ### Serial port automation Access VM serial ports for console automation and monitoring. ```applescript tell application "UTM" set vm to first virtual machine whose name is "Linux Server" -- Get serial ports set ports to every serial port of vm repeat with port in ports log "Port mode: " & mode of port if mode of port is "TCP" then log "TCP host: " & tcp host of port log "TCP port: " & tcp port of port end if end repeat -- Connect to serial port externally -- Use: nc localhost 4555 end tell ``` ## QEMU Argument Generation ### Using the argument builder Generate type-safe QEMU command-line arguments using Swift result builders. ```swift import Foundation func buildQEMUArguments(config: UTMQemuConfiguration) -> [String] { let builder = QEMUArgumentBuilder.buildFinalResult { // Machine type "-machine" config.system.target.rawValue // CPU configuration "-cpu" config.system.cpu.rawValue // Add CPU flags conditionally if !config.system.cpuFlagsAdd.isEmpty { for flag in config.system.cpuFlagsAdd { "\(flag.rawValue),+" } } // Memory "-m" String(config.system.memorySize) // CPU count if config.system.cpuCount > 0 { "-smp" String(config.system.cpuCount) } // Drives for (index, drive) in config.drives.enumerated() { if drive.imageType == .disk { "-drive" "file=\(drive.imageName ?? ""),if=\(drive.interface.rawValue),format=qcow2" } else if drive.imageType == .cd { "-cdrom" drive.imageName ?? "" } } // Network devices for network in config.networks { "-netdev" "user,id=net\(network.id)" "-device" "\(network.hardware.rawValue),netdev=net\(network.id),mac=\(network.macAddress)" } // Display if !config.displays.isEmpty { "-vga" "std" "-display" "cocoa" } } return builder.map { $0.string } } // Usage let config = UTMQemuConfiguration() config.system.architecture = .x86_64 config.system.target = QEMUTarget_x86_64.q35 config.system.cpu = QEMUCPU_x86_64.host config.system.memorySize = 4096 let args = buildQEMUArguments(config: config) print("QEMU arguments: \(args.joined(separator: " "))") // Output: // qemu-system-x86_64 -machine q35 -cpu host -m 4096 -vga std -display cocoa ``` ### Custom QEMU options Add custom QEMU arguments for advanced configurations not covered by the UI. ```swift // Extend configuration with raw QEMU arguments var qemuConfig = UTMQemuConfiguration() qemuConfig.qemu.additionalArguments = [ QEMUArgument(rawValue: "-device"), QEMUArgument(rawValue: "intel-hda"), QEMUArgument(rawValue: "-device"), QEMUArgument(rawValue: "hda-output"), QEMUArgument(rawValue: "-global"), QEMUArgument(rawValue: "ICH9-LPC.disable_s3=1") ] // Custom TPM device qemuConfig.qemu.additionalArguments.append(contentsOf: [ QEMUArgument(rawValue: "-chardev"), QEMUArgument(rawValue: "socket,id=chrtpm,path=/tmp/swtpm-sock"), QEMUArgument(rawValue: "-tpmdev"), QEMUArgument(rawValue: "emulator,id=tpm0,chardev=chrtpm"), QEMUArgument(rawValue: "-device"), QEMUArgument(rawValue: "tpm-tis,tpmdev=tpm0") ]) // Enable QEMU monitor on TCP qemuConfig.qemu.hasMonitor = true qemuConfig.qemu.monitorMode = .tcpServer qemuConfig.qemu.monitorHost = "localhost" qemuConfig.qemu.monitorPort = 4444 let vm = try UTMQemuVirtualMachine( packageUrl: URL(fileURLWithPath: "/path/to/VM.utm"), configuration: qemuConfig, isShortcut: false ) try await vm.save() ``` ## Network Configuration ### Advanced network setup with VLAN Configure virtual networks with custom VLAN settings and port forwarding. ```swift var network = UTMQemuConfigurationNetwork() // Emulated mode with custom VLAN network.mode = .emulated network.hardware = QEMUNetworkDevice_x86_64.virtio network.macAddress = "52:54:00:AB:CD:EF" // VLAN configuration network.vlanGuestAddress = "10.0.2.15" network.vlanHostAddress = "10.0.2.2" network.vlanDhcpStartAddress = "10.0.2.15" network.vlanDhcpDomain = "utm.local" network.vlanDnsServerAddress = "10.0.2.3" // Isolate from host network.isIsolateFromHost = false // Port forwarding rules var httpForward = UTMQemuConfigurationPortForward() httpForward.protocol = .tcp httpForward.guestAddress = "10.0.2.15" httpForward.guestPort = 80 httpForward.hostAddress = "127.0.0.1" httpForward.hostPort = 8080 var httpsForward = UTMQemuConfigurationPortForward() httpsForward.protocol = .tcp httpsForward.guestPort = 443 httpsForward.hostPort = 8443 var sshForward = UTMQemuConfigurationPortForward() sshForward.protocol = .tcp sshForward.guestPort = 22 sshForward.hostPort = 2222 network.portForward = [httpForward, httpsForward, sshForward] // Add to configuration config.networks = [network] print("Network configured:") print("- Guest can access: http://localhost:8080 -> VM:80") print("- SSH access: ssh -p 2222 user@localhost") ``` ### Bridged networking Configure bridged networking for direct network access (requires privileges). ```swift // Bridged network configuration var bridgedNet = UTMQemuConfigurationNetwork() bridgedNet.mode = .bridged bridgedNet.hardware = QEMUNetworkDevice_x86_64.e1000 bridgedNet.macAddress = "52:54:00:12:34:56" bridgedNet.bridgeInterface = "en0" // Ethernet interface name config.networks = [bridgedNet] // Note: Bridged networking requires elevated privileges // QEMUHelper will prompt for authorization ``` ## Guest Agent Integration ### Executing commands in guest OS Use the QEMU guest agent to execute commands and transfer files. ```swift // Access guest agent from running QEMU VM if let qemuVM = vm as? UTMQemuVirtualMachine, let guestAgent = await qemuVM.guestAgent { // Execute command in guest let result = try await guestAgent.execute( path: "/usr/bin/ls", arguments: ["-la", "/home"], environment: ["LANG": "en_US.UTF-8"] ) if let output = result.output { print("Command output: \(output)") } if let exitCode = result.exitCode { print("Exit code: \(exitCode)") } // Get guest network interfaces let interfaces = try await guestAgent.getNetworkInterfaces() for interface in interfaces { print("Interface: \(interface.name)") for addr in interface.ipAddresses ?? [] { print(" IP: \(addr.address)") } } // Write file to guest let fileContents = "Hello from host!".data(using: .utf8)! try await guestAgent.writeFile( at: "/tmp/message.txt", contents: fileContents ) // Read file from guest let guestFile = try await guestAgent.readFile( at: "/etc/hostname" ) if let hostname = String(data: guestFile, encoding: .utf8) { print("Guest hostname: \(hostname)") } } ``` ## Drive Management ### Hot-swapping removable drives Change CD-ROM and other removable drives while the VM is running. ```swift // Eject CD-ROM if let qemuVM = vm as? UTMQemuVirtualMachine { let cdDrive = qemuVM.config.drives.first { $0.imageType == .cd } if let drive = cdDrive { try await qemuVM.eject(drive) print("CD ejected") // Insert new disc let newIsoUrl = URL(fileURLWithPath: "/path/to/new-disc.iso") try await qemuVM.changeMedium(drive, to: newIsoUrl) print("New disc inserted") } } // Update external drive in registry let driveId = "external-001" let newDriveUrl = URL(fileURLWithPath: "/path/to/new-external.img") vm.registryEntry.externalDrives[driveId] = UTMRegistryEntry.File( path: newDriveUrl.path, bookmark: try? newDriveUrl.bookmarkData( options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil ), isValid: true ) // Reload VM configuration to pick up changes vm.updateConfigFromRegistry() ``` UTM provides a comprehensive virtualization platform with flexible backends, extensive configuration options, and multiple automation interfaces. The protocol-based architecture enables seamless integration of QEMU emulation and Apple's native Virtualization framework, allowing developers to choose the best backend for their use case. Remote VDI capabilities extend VM access across networks with encryption and authentication, while CLI and scripting interfaces enable automation workflows. The configuration system uses Codable property lists for persistence, security-scoped bookmarks for sandboxed file access, and a separate registry for runtime metadata. State management leverages Swift's async/await for responsive operations and Combine's @Published properties for reactive UI updates. The QEMU argument builder uses result builders for type-safe command generation, and the guest agent protocol enables file transfers and command execution. These patterns combine to create a maintainable, extensible virtualization platform suitable for both interactive use and automated deployment scenarios.