How to Use vphone-cli for End-to-End E2E Testing of iOS VMs
vphone-cli enables end-to-end E2E testing of iOS VMs by orchestrating three phases: provisioning patched firmware via VPhoneFirmwareSelection.swift and FirmwarePatcher, booting a VZVirtualMachine with a vsock device on port 1337, and driving UI interactions through the vphoned daemon using a length-prefixed JSON protocol with unique request-IDs.
vphone-cli is a full-stack virtualization framework for Apple Silicon that creates programmable iPhone virtual machines for automated testing workflows. By combining firmware patching, DFU restoration, and a host-control socket architecture, it exposes deterministic iOS environments to external test harnesses through a structured communication protocol. This guide explains how to implement complete E2E testing pipelines using the Swift core and command-line interfaces according to the Lakr233/vphone-cli source code.
Three-Phase E2E Testing Architecture
End-to-end testing with vphone-cli follows a strict lifecycle comprising VM provisioning, boot configuration, and test driver execution. Each phase maps to specific source files in the repository.
Phase 1: VM Provisioning
The provisioning phase prepares a custom firmware bundle through four sequential operations. First, fw prepare in VPhoneFirmwareSelection.swift downloads the target IPSW and merges the iPhone filesystem with cloudOS components. Next, fw patch invokes the FirmwarePatcher/* modules—such as KernelJBPatcher.swift—to apply firmware variants like jb (jailbreak) or exp (experimental) to the kernel image.
The process continues with restore operations defined in VPhoneRestoreOps.swift, which performs a DFU-mode restoration of the VM's root filesystem. Finally, cfw install via VPhoneFWCLI.swift mounts the custom firmware on the host, making it available for the virtual machine bundle.
Phase 2: VM Boot Configuration
Once provisioned, VPhoneVirtualMachine.swift constructs the virtual machine environment by creating a VZVirtualMachineConfiguration object. This configuration attaches a VZVirtioSocketDevice configured for port 1337, establishing the communication channel between host and guest. The VM launches with the patched kernel image and an optional USB touch injection device, though iOS 18-based VMs require guest-side touch handling.
The boot process exposes a Unix-domain host-control socket at <bundle>/vphone.sock, enabling external test runners to interface with the running VM without direct Swift dependencies.
Phase 3: Test Driver Integration
After the VM boots, the guest-side vphoned daemon (compiled via scripts/build.sh) listens on the vsock port and advertises capabilities through a caps handshake. The host-side client implemented in VPhoneControl.swift connects to this socket and communicates via a length-prefixed JSON protocol where every request includes a unique request-ID and awaits a typed response.
Key protocol operations include:
sendTouchfor HID event injection (withuseGuestTouchInjectionenabled for iOS 18+)sendRequestwith typescreenshotfor UI captureinstallIPAfor application deploymentappLaunchwith bundle ID executionaccessibilityTreefor UI hierarchy inspection
Implementing E2E Test Workflows
You can automate E2E tests using either the Swift SDK for type-safe integration or shell scripts for CI pipelines.
Swift SDK Approach
The following Swift example demonstrates the complete lifecycle from VM creation to UI assertion:
import VPhoneCore
import VPhoneCLI
// 1️⃣ Create (or reuse) a VM called “e2e‑test”
let vmName = "e2e-test"
let vm = try VPhoneVirtualMachine(name: vmName, variant: .jb)
try vm.create()
try vm.prepare(iphoneVersion: "26.1")
try vm.patch()
try vm.restore()
try vm.installCFW()
try vm.launch()
// 2️⃣ Connect to the guest daemon
let control = VPhoneControl(variant: .jb)
control.connect(device: vm.vsockDevice)
// 3️⃣ Execute test sequence
Task {
// Verify touch injection capability
if control.useGuestTouchInjection {
await control.sendTouch(phase: 0, x: 0.5, y: 0.5) // down
await control.sendTouch(phase: 3, x: 0.5, y: 0.5) // up
}
// Take screenshot for AI verification
let (_, screenshot) = try await control.sendRequest(["t": "screenshot"])
// Install and launch app
let ipaURL = URL(fileURLWithPath: "/path/to/MyApp.ipa")
let result = try await control.installIPA(localURL: ipaURL)
let pid = try await control.appLaunch(bundleId: "com.example.myapp")
// Verify UI state
let tree = try await control.accessibilityTree()
// Clean-up
await control.disconnect()
}
CLI Automation Approach
For shell-based CI pipelines, the equivalent workflow uses the vphone-cli binary:
# 1️⃣ Provision the VM
vphone-cli vm create e2e-test -V jb
# 2️⃣ Launch the VM (background)
vphone-cli vm launch e2e-test --dfu &
# 3️⃣ Wait for guest handshake (poll until ready)
# vphone-cli automatically reconnects; verify with `vphone-cli vm info`
# 4️⃣ Inject touch events at screen center
vphone-cli control touch --phase down --x 0.5 --y 0.5
vphone-cli control touch --phase up --x 0.5 --y 0.5
# 5️⃣ Capture screenshot for diffing
vphone-cli control screenshot > screen.png
# 6️⃣ Install IPA and launch application
vphone-cli install myapp.ipa
vphone-cli control app launch --bundle-id com.example.myapp
Key Implementation Files
| File | Responsibility |
|---|---|
VPhoneVirtualMachine.swift |
Orchestrates VZVirtualMachineConfiguration, vsock device attachment on port 1337, and lifecycle management. |
VPhoneControl.swift |
Implements the host-side JSON protocol client with methods like sendRequest(), sendTouch(), and installIPA(). |
VPhoneFWCLI.swift |
Exposes fw subcommands (prepare, patch) that drive the firmware modification pipeline. |
FirmwarePatcher/Kernel/KernelJBPatcher.swift |
Applies jailbreak and experimental patches to kernel images based on selected variants. |
VPhoneRestoreOps.swift |
Handles DFU restoration sequences for the VM filesystem. |
scripts/vphoned |
Guest-side Objective-C daemon listening on vsock port 1337, implementing screenshot capture and touch injection. |
Summary
- VM Provisioning requires executing
prepare,patch,restore, andinstallCFWoperations throughVPhoneFirmwareSelection.swiftandVPhoneFWCLI.swiftto generate a bootable patched firmware. - VM Boot uses
VPhoneVirtualMachine.swiftto configure aVZVirtualMachinewith aVZVirtioSocketDeviceon port 1337, exposing control via Unix socket. - Test Execution relies on
VPhoneControl.swiftcommunicating with thevphoneddaemon via a length-prefixed JSON protocol supporting screenshots, touch injection (necessary for iOS 18), IPA installation, and accessibility queries. - Both Swift and CLI interfaces provide deterministic automation suitable for headless CI environments on Apple Silicon hosts with relaxed SIP/AMFI settings.
Frequently Asked Questions
What hardware is required for vphone-cli E2E testing?
vphone-cli requires Apple Silicon Macs (M1 or later) with SIP (System Integrity Protection) and AMFI (Apple Mobile File Integrity) relaxed or disabled. The host must run macOS with the Virtualization framework available, as VPhoneVirtualMachine.swift depends on VZVirtualMachine and related private APIs declared in Package.swift.
How does the JSON protocol ensure request/response correlation?
According to VPhoneControl.swift, every command sent to the vphoned daemon includes a unique request-ID field in the JSON payload. The daemon echoes this identifier in its response, allowing the host-side sendRequest() method to match asynchronous replies with their originating commands over the vsock connection.
Why is useGuestTouchInjection necessary for iOS 18 VMs?
In iOS 18-based virtual machines, the native VZUSBPointerDevice path stops delivering digitizer events to the guest OS. As implemented in VPhoneControl.swift, the useGuestTouchInjection property enables the fallback mechanism where touch coordinates are forwarded through the vsock JSON protocol to the vphoned daemon, which injects HID events directly into the guest's input subsystem.
Can vphone-cli integrate with existing CI/CD pipelines?
Yes. The tool supports headless operation through the CLI interface, and the host-control socket (vphone.sock) allows external test harnesses like vphone-mcp to drive VMs without Swift dependencies. Because all interactions use deterministic JSON commands and inline screenshot capture, you can incorporate vphone-cli into GitHub Actions, Jenkins, or custom runners on Apple Silicon build agents.
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 →