How the Dynamic Library Facilitates Private API Access for VZVirtualMachine Configuration in vphone-cli
The Dynamic Swift package enables private Virtualization.framework API access by using runtime Objective-C introspection to locate, instantiate, and message private classes without compile-time linkage, allowing vphone-cli to configure unsupported VM features like neural engine accelerators and GDB debug stubs while building cleanly against the public SDK.
The vphone-cli project by Lakr233 requires advanced Virtualization.framework capabilities—such as VZMacVideoToolboxDeviceConfiguration and VZUSBTouchScreenConfiguration—that Apple excludes from the public Swift SDK. Rather than using undocumented headers that would break compilation, the project imports the Dynamic package to bridge these private symbols at runtime.
Why Virtualization.framework Requires Private API Access
Apple’s Virtualization.framework exposes only a subset of its capabilities in the public SDK. Features like the Neural Engine accelerator, USB touch screens, synthetic batteries, and GDB debug stubs exist in the framework binaries but lack public Swift interfaces. Attempting to import these classes directly results in compilation errors because the symbols are not present in the standard SDK headers.
The vphone-cli codebase solves this by treating the public VZVirtualMachineConfiguration as a foundation while injecting privately-configured objects through runtime messaging. This approach maintains source compatibility with the public SDK while unlocking the full hardware virtualization feature set at runtime.
How Dynamic Enables Runtime Private API Access
The Dynamic package (declared as a dependency in Package.swift) provides a thin wrapper around Objective-C runtime functions. It operates through four core mechanisms:
Runtime Symbol Resolution
Dynamic locates private classes without compile-time linking by using NSClassFromString, objc_getClass, or Swift’s Mirror API. This allows the code to obtain references to classes like VZMacHardwareModelDescriptor even when they are absent from the public headers.
Object Instantiation via alloc/init
Once a class reference is obtained, Dynamic creates instances by sending alloc and init messages through the Objective-C runtime. The wrapper exposes factory methods such as Dynamic._VZMacVideoToolboxDeviceConfiguration() that return typed Dynamic objects wrapping the raw AnyObject.
Typed Property Accessors
After instantiation, Dynamic provides type-safe accessors—including asObject, asInt, and asUInt64—to read and write properties on the underlying private object. This bridges the gap between the runtime AnyObject reference and Swift’s type system.
Selector-Based Method Invocation
Dynamic translates method calls like Dynamic(config)._setAcceleratorDevices([...]) into Objective-C runtime messages targeting hidden selectors such as setAcceleratorDevices:. This allows the code to invoke private setters on VZVirtualMachineConfiguration instances without static typing.
Configuring VM Components Through Dynamic
In sources/vphone-cli/VPhoneVirtualMachine.swift, the project constructs the entire VM configuration graph using Dynamic helpers. The following patterns demonstrate how specific hardware features are configured:
Accelerator Devices
The code instantiates three private accelerator configurations and attaches them to the VM configuration:
let video = Dynamic._VZMacVideoToolboxDeviceConfiguration().asObject as! VZMacVideoToolboxDeviceConfiguration
let neural = Dynamic._VZMacNeuralEngineDeviceConfiguration().asObject as! VZMacNeuralEngineDeviceConfiguration
let scaler = Dynamic._VZMacScalerAcceleratorDeviceConfiguration().asObject as! VZMacScalerAcceleratorDeviceConfiguration
Dynamic(config)._setAcceleratorDevices([video, neural, scaler])
USB Touch Input
Touch screen support requires instantiating VZUSBTouchScreenConfiguration and attaching it via the private setMultiTouchDevices: selector:
if let touch = Dynamic._VZUSBTouchScreenConfiguration().asObject as? VZUSBTouchScreenConfiguration {
Dynamic(config)._setMultiTouchDevices([touch])
}
Synthetic Battery and Power Management
The project creates a synthetic battery source that can be updated at runtime:
let batterySrc = Dynamic._VZMacSyntheticBatterySource()
let batteryCfg = Dynamic._VZMacBatteryPowerSourceDeviceConfiguration().asObject as! VZMacBatteryPowerSourceDeviceConfiguration
Dynamic(config)._setPowerSourceDevices([batteryCfg])
Dynamic(batterySrc).setCharge(100)
Dynamic(batterySrc).setConnectivity(.charging)
Debug and Security Components
For debugging, the code creates a GDB stub on a specified port:
if let stub = Dynamic._VZGDBDebugStubConfiguration(port: 1234).asObject {
Dynamic(config)._setDebugStub(stub)
}
Secure Enclave Processor (SEP) configuration follows a similar pattern using Dynamic._VZSEPCoprocessorConfiguration(storageURL:) and Dynamic(config)._setCoprocessors([sepObj]).
Handling Input Events with Dynamic
Beyond configuration, vphone-cli uses Dynamic to inject runtime events into the VM. In sources/vphone-cli/VPhoneKeyHelper.swift, keyboard events are synthesized by calling private initializers:
func sendKeyEvent(to vm: VZVirtualMachine, keyCode: UInt16, isDown: Bool) {
let type: UInt8 = isDown ? 0 : 1
guard let keyEvent = Dynamic._VZKeyEvent(type: type, keyCode: keyCode).asAnyObject else { return }
guard let keyboards = Dynamic(vm)._keyboards.asObject as? NSArray,
let keyboard = keyboards.firstObject else { return }
Dynamic(keyboard).sendKeyEvents([keyEvent] as NSArray)
}
Similarly, sources/vphone-cli/VPhoneVirtualMachineView.swift forwards touch events using Dynamic._VZTouch and Dynamic._VZMultiTouchEvent to support multi-touch input on the virtual display.
Key Source Files Implementing Dynamic
The following files in the Lakr233/vphone-cli repository demonstrate systematic use of the Dynamic wrapper:
sources/vphone-cli/VPhoneVirtualMachine.swift– Constructs the full VM configuration, heavily using Dynamic for private accelerator, battery, and debug stub objects.sources/vphone-cli/VPhoneHardwareModel.swift– Creates hardware model descriptors viaDynamic._VZMacHardwareModelDescriptor.sources/vphone-cli/VPhoneVirtualMachineView.swift– Forwards touch events to the VM usingDynamic._VZTouchand multi-touch event constructors.sources/vphone-cli/VPhoneKeyHelper.swift– Injects keyboard events usingDynamic._VZKeyEvent.research/VPhoneVirtualMachineRefactored.swift– Detailed walkthrough showing explicit Dynamic calls for educational reference.Package.swift– Declares the external dependency on the Dynamic package.
Summary
- Runtime Discovery: The Dynamic package uses
NSClassFromStringandobjc_getClassto locate private Virtualization.framework classes without compile-time linkage. - Clean Compilation: All private API calls route through Dynamic, allowing
vphone-clito build against the public SDK while accessing hidden features. - Full Hardware Support: Through Dynamic, the project configures Neural Engine accelerators, video scalers, USB touch screens, synthetic batteries, GDB debug stubs, and SEP coprocessors.
- Event Injection: Dynamic enables runtime injection of keyboard and touch events into the VM by calling private initializers and methods.
Frequently Asked Questions
How does Dynamic avoid compilation errors when using private APIs?
Dynamic avoids compilation errors by never directly referencing private classes in the source code. Instead, it looks up classes by string name at runtime using NSClassFromString and performs all object creation and method invocation through the Objective-C runtime. The Swift compiler only sees references to the Dynamic wrapper type, which is fully public.
What specific VZVirtualMachine features does vphone-cli unlock with Dynamic?
According to the source code in VPhoneVirtualMachine.swift, the project unlocks Neural Engine acceleration, Mac Video Toolbox encoding, USB touch screen input, synthetic battery management, GDB debug stub attachment, and Secure Enclave Processor (SEP) configuration. These features are unavailable through the public Virtualization.framework API.
Is using Dynamic to access private APIs safe for production use?
While Dynamic enables powerful capabilities, accessing private APIs carries risks. Apple may change or remove private classes in future macOS updates, causing runtime crashes. The vphone-cli project mitigates this by checking for nil returns from Dynamic constructors and gracefully degrading features when private symbols are unavailable.
How does vphone-cli send keyboard events to the virtual machine?
The project uses Dynamic._VZKeyEvent(type:keyCode:) to instantiate private key event objects, then retrieves the VM's keyboard array via Dynamic(vm)._keyboards and calls the private sendKeyEvents: method. This implementation resides in VPhoneKeyHelper.swift and allows injection of arbitrary key codes without public API support.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →