# How to Use Virtualization.framework with Swift on Apple Silicon: A Complete Guide

> Learn how to use Virtualization.framework with Swift on Apple Silicon. Build macOS and Linux VMs programmatically with VZVirtualMachine and start them with platform-specific options.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: how-to-guide
- Published: 2026-09-09

---

**Virtualization.framework on Apple Silicon lets you create macOS and Linux VMs in Swift by configuring a `VZVirtualMachine`, validating it, and calling `start()` with platform-specific options.**

The `Lakr233/vphone-cli` repository provides a production-ready reference implementation. It demonstrates every step from hardware model creation to UI integration using pure Swift and Apple's native virtualization APIs. This guide walks through the same architecture used to run a virtual iPhone on Apple Silicon Macs.

---

## Prerequisites and Platform Requirements

Virtualization.framework is **Apple Silicon only**. The `VZMac*` classes used throughout this guide are unavailable on Intel Macs.

Your project must target:

- **macOS 15 or later** (Sequoia)
- **Apple Silicon CPU** (M1/M2/M3/M4 series)
- **Swift 6** and Xcode 15+

The `vphone-cli` source enforces these requirements at runtime by checking `ProcessInfo.processInfo.isOperatingSystemAtLeast` and verifying the hardware model identifier (PV = 3) exists.

---

## Step 1: Import the Framework

Every file interacting with the VM begins with the framework import. In [`VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachine.swift), this single line enables the entire API surface:

```swift
import Virtualization

```

No bridging header or C++ interop is required. Virtualization.framework is a first-class Swift framework on macOS 15.

---

## Step 2: Create the Hardware Model

Apple Silicon VMs require a `VZMacHardwareModel` that defines the virtual platform version. The `vphone-cli` project uses PV = 3, which corresponds to the virtual iPhone hardware:

```swift
// VPhoneVirtualMachine.swift:49-52
let hardwareModel = try VPhoneHardware.createModel()

```

A hardware model is **immutable once created**. You persist it alongside your VM and reuse it for subsequent launches. Changing hardware models mid-lifecycle invalidates the VM state.

---

## Step 3: Manage Machine Identity and NVRAM

Every macOS VM needs a stable `VZMacMachineIdentifier` and auxiliary NVRAM storage. The identifier must persist across reboots; NVRAM stores boot arguments and firmware variables.

In `VPhoneVirtualMachine.swift:55-84`, the code either loads an existing identifier from a manifest or generates a fresh one:

```swift
let machineIdentifier = VZMacMachineIdentifier()

// Create auxiliary storage for NVRAM variables
let auxStorage = try VZMacAuxiliaryStorage(
    creatingStorageAt: storageURL,
    hardwareModel: hardwareModel,
    options: .allowOverwrite
)

```

The project then writes boot arguments to enable serial console output (`VPhoneVirtualMachine.swift:36-50`):

```swift
let bootArgs = "serial=3 debug=0x104c04"
// Private API usage in vphone-cli; production code uses setData:forKey:
Dynamic(auxStorage)._setDataValue(
    bootArgs.data(using: .utf8)!,
    forNVRAMVariableNamed: "boot-args"
)

```

---

## Step 4: Configure the macOS Platform

The `VZMacPlatformConfiguration` ties together hardware model, machine identifier, and NVRAM. This is specific to Apple Silicon VMs—Linux VMs use `VZGenericPlatformConfiguration` instead.

From `VPhoneVirtualMachine.swift:106-135`:

```swift
let platform = VZMacPlatformConfiguration()
platform.hardwareModel = hardwareModel
platform.machineIdentifier = machineIdentifier
platform.auxiliaryStorage = auxStorage

```

Platform configuration is validated automatically when you build the final VM configuration. Mismatched hardware models and identifiers produce clear error messages at validation time.

---

## Step 5: Build the Complete VM Configuration

`VZVirtualMachineConfiguration` aggregates all device and resource settings. The `vphone-cli` implementation (`VPhoneVirtualMachine.swift:152-224`) demonstrates a comprehensive setup:

### CPU and Memory

```swift
let config = VZVirtualMachineConfiguration()

// Enforce minimums automatically
config.cpuCount = max(4, VZVirtualMachineConfiguration.minimumAllowedCPUCount)
config.memorySize = max(4 * 1024 * 1024 * 1024,
                       VZVirtualMachineConfiguration.minimumAllowedMemorySize)

```

### Graphics Display

```swift
let graphics = VZMacGraphicsDeviceConfiguration()
graphics.displays = [
    VZMacGraphicsDisplayConfiguration(
        widthInPixels: 1290,
        heightInPixels: 2796,
        pixelsPerInch: 460  // iPhone Retina density
    )
]
config.graphicsDevices = [graphics]

```

### Audio Devices

```swift
let sound = VZVirtioSoundDeviceConfiguration()
sound.streams = [
    VZVirtioSoundDeviceInputStreamConfiguration(),   // host mic → guest
    VZVirtioSoundDeviceOutputStreamConfiguration()   // guest audio → host speakers
]
config.audioDevices = [sound]

```

### Storage

```swift
let diskAttachment = try VZDiskImageStorageDeviceAttachment(
    url: diskURL,
    readOnly: false
)
let disk = VZVirtioBlockDeviceConfiguration(attachment: diskAttachment)
config.storageDevices = [disk]

```

### Serial Port (PL011 UART)

The virtual iPhone uses a PL011 serial controller for console access. `vphone-cli` accesses this through private API wrappers:

```swift
// Private API: _VZPL011SerialPortConfiguration
let serial = Dynamic._VZPL011SerialPortConfiguration().asObject as! VZSerialPortConfiguration

let pipeIn = Pipe()
let pipeOut = Pipe()
serial.attachment = VZFileHandleSerialPortAttachment(
    fileHandleForReading: pipeIn.fileHandleForReading,
    fileHandleForWriting: pipeOut.fileHandleForWriting
)
config.serialPorts = [serial]

```

### Optional: VSocket for Host-Guest Communication

```swift
config.socketDevices = [VZVirtioSocketDeviceConfiguration()]

```

This creates a vsock channel used by `vphone-cli`'s control daemon (`vphoned`) to exchange JSON commands between host and guest.

---

## Step 6: Validate and Instantiate the VM

Configuration errors surface during validation, before any resources are allocated. From `VPhoneVirtualMachine.swift:291-296`:

```swift
try config.validate()

let virtualMachine = VZVirtualMachine(configuration: config)

```

`validate()` throws `VZError` with specific codes for unsupported configurations—helpful for debugging memory size mismatches or unavailable device types.

---

## Step 7: Start the VM with Options

Apple Silicon VMs support platform-specific start options. The `vphone-cli` project implements DFU mode forcing for firmware development:

```swift
let startOptions = VZMacOSVirtualMachineStartOptions()

// Private API for DFU mode; standard API available for normal boot
Dynamic(startOptions)._setForceDFU(true)

try await virtualMachine.start(options: startOptions)

```

The `start(options:)` method is asynchronous and throws on immediate failures. Guest crashes or panics are reported through the delegate instead.

---

## Step 8: Handle VM Lifecycle Events

Assign a `VZVirtualMachineDelegate` to respond to guest state changes. `VPhoneVirtualMachine.swift:81-97` adopts this protocol:

```swift
extension VPhoneVirtualMachine: VZVirtualMachineDelegate {
    func guestDidStop(_ virtualMachine: VZVirtualMachine) {
        // Clean up resources, notify UI
    }
    
    func virtualMachine(_ vm: VZVirtualMachine, didStopWithError error: Error) {
        // Log crash details, offer restart
    }
}

```

Delegate methods execute on the VM's internal queue. Dispatch to the main queue for UI updates.

---

## Step 9: Integrate with AppKit UI

Display the VM in a native window using `VZVirtualMachineView`. The [`VPhoneAppDelegate.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneAppDelegate.swift) file orchestrates this:

```swift
let vmView = VZVirtualMachineView()
vmView.virtualMachine = virtualMachine

let window = NSWindow(
    contentRect: NSRect(x: 0, y: 0, width: 720, height: 1440),
    styleMask: [.titled, .closable, .resizable],
    backing: .buffered,
    defer: false
)
window.contentView = vmView
window.makeKeyAndOrderFront(nil)

```

`VZVirtualMachineView` handles input forwarding, display scaling, and cursor capture automatically. Subclass it to intercept keyboard events for special key combinations, as [`VPhoneVirtualMachineView.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneVirtualMachineView.swift) demonstrates.

---

## Advanced: Synthetic Devices and Debugging

The `vphone-cli` source includes several sophisticated configurations worth studying:

| Feature | Implementation | Source Location |
|---------|---------------|---------------|
| Synthetic battery | `VZMacSyntheticBatterySource` | `VPhoneVirtualMachine.swift:47-60` |
| GDB debug stub | `VZGDBDebugStubConfiguration` | `VPhoneVirtualMachine.swift:47-60` |
| USB touch screen | `VZUSBTouchScreenConfiguration` | `VPhoneVirtualMachine.swift:47-60` |

These are optional but demonstrate the full capability of Virtualization.framework on Apple Silicon.

---

## Summary

- **Import `Virtualization.framework`** and target macOS 15+ on Apple Silicon only
- **Create `VZMacHardwareModel`** with your platform version, then persist it
- **Generate `VZMacMachineIdentifier`** and `VZMacAuxiliaryStorage` for NVRAM
- **Assemble `VZMacPlatformConfiguration`** linking all platform components
- **Build `VZVirtualMachineConfiguration`** with CPU, memory, graphics, storage, audio, serial, and network devices
- **Validate with `config.validate()`** before instantiating `VZVirtualMachine`
- **Start asynchronously** using `VZMacOSVirtualMachineStartOptions` for platform-specific behavior
- **Implement `VZVirtualMachineDelegate`** to respond to guest lifecycle events
- **Display with `VZVirtualMachineView`** in an `NSWindow` for interactive use

The `Lakr233/vphone-cli` repository provides working implementations of every step above, making it the definitive reference for Virtualization.framework with Swift on Apple Silicon.

---

## Frequently Asked Questions

### What makes Virtualization.framework Apple Silicon only?

The `VZMacHardwareModel`, `VZMacPlatformConfiguration`, and related classes implement Apple's proprietary ARM virtualization extensions. These leverage the Apple Silicon CPU's EL2 hypervisor mode and memory architecture. Intel Macs expose different virtualization capabilities through Hypervisor.framework instead.

### Can I run Linux VMs with the same code?

Yes, with modifications. Linux guests use `VZLinuxBootLoader` instead of `VZMacOSBootLoader`, and `VZGenericPlatformConfiguration` instead of `VZMacPlatformConfiguration`. The device configuration (Virtio block, network, serial) remains largely identical. The `vphone-cli` project specifically targets macOS guests with PV=3 hardware.

### How do I persist VM state between launches?

Save the `VZMacMachineIdentifier` data and reuse the same `VZMacHardwareModel` when recreating the configuration. The NVRAM file (`VZMacAuxiliaryStorage`) must also persist at a stable path. Do not regenerate identifiers—doing so presents a new virtual machine to the guest OS, invalidating activation state and licenses.

### Why does my VM fail validation with "configuration is not supported"?

Check three common causes: insufficient host memory for requested `memorySize`, `cpuCount` below `minimumAllowedCPUCount`, or attempting to use `VZMacGraphicsDeviceConfiguration` on an incompatible hardware model. Validation errors include specific `VZErrorCode` values that pinpoint the failing configuration section.