# How useGuestTouchInjection Selects the Touch Path Based on Guest iOS Version

> Discover how useGuestTouchInjection selects touch paths for iOS guest versions. Learn how it routes older kernels via Virtualization framework multitouch and iOS 26+ via guest-side HID injection.

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

---

**The `useGuestTouchInjection` computed property returns `true` only when the guest iOS major version is less than 26, routing older kernels through native Virtualization framework multitouch while forcing iOS 26+ to use guest-side HID injection via the `vphoned` daemon.**

In the **Lakr233/vphone-cli** repository, touch input handling depends on a version-aware flag that determines whether macOS processes multitouch events natively or delegates them to a daemon running inside the guest VM. This logic centers on the `useGuestTouchInjection` property, which inspects the guest's reported iOS version during the handshake phase and switches touch delivery mechanisms accordingly. Understanding this behavior is essential for debugging input issues when virtualizing newer iOS versions that run on the 26.x kernel.

## The Version Detection Logic in VPhoneControl

The decision point resides in [`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift) within the `useGuestTouchInjection` computed property (lines 35‑44). This accessor parses the `guestIOSVersion` string received during VM initialization and extracts the major version number to apply a hard threshold.

```swift
// VPhoneControl.swift – lines 35-44
var useGuestTouchInjection: Bool {
    guard isConnected,
          guestCaps.contains("touch"),
          let major = guestIOSVersion.flatMap({ Int($0.split(separator: ".").first ?? "") })
    else { return false }
    return major < 26          // iOS < 26 → use guest-side injection
}

```

The property returns `true` only when three conditions converge:

- The host maintains an active connection (`isConnected`).
- The guest capability set includes the **"touch"** feature (`guestCaps.contains("touch")`).
- The parsed major version integer is strictly less than **26**.

If any check fails, the method returns `false`, signaling the system to use the native Virtualization framework path.

## Why iOS 26+ Requires Guest-Side Injection

**iOS 18 and later** builds run on the **26.x kernel**, which alters how the Virtualization framework processes USB HID reports. On these newer kernels, the USB-touchscreen driver extension (dext) receives raw HID reports but **does not emit digitizer events** that macOS can translate into `VZMultiTouchEvent` objects.

Consequently, the standard `VZMultiTouchEvent` dispatched from the host never reaches the iOS UI layer. To circumvent this limitation, **vphone-cli** falls back to **guest-side HID injection** performed by the `vphoned` daemon running inside the VM. Older kernels (iOS < 26) continue to generate digitizer events correctly, allowing the native VZ multitouch path to function without intervention.

## Touch Routing in VPhoneVirtualMachineView

The view layer consumes this flag in [`sources/vphone-cli/VPhoneVirtualMachineView.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneVirtualMachineView.swift) (lines 58‑66) to decide how to forward touch events. When `useGuestTouchInjection` evaluates to `true`, the view bypasses the Virtualization framework and sends coordinates directly to the guest daemon.

```swift
// VPhoneVirtualMachineView.swift – lines 58-65
if let control, control.useGuestTouchInjection {
    control.sendTouch(phase: phase,
                      x: Double(normalizedPoint.x),
                      y: Double(normalizedPoint.y))
    return true                     // Guest-side injection
}

```

If the property returns `false`, the code constructs a `VZTouch` object and forwards it via `Dynamic._VZMultiTouchEvent`, leveraging the standard Apple Virtualization APIs. This branching logic ensures compatibility across both legacy and modern iOS bases without requiring manual configuration.

## Practical Implementation Examples

When building automation tools or debugging touch input, you can inspect the flag directly from the host side before initiating a gesture sequence:

```swift
let control = VPhoneControl(...)
if control.useGuestTouchInjection {
    // Send a single-finger touch through the guest daemon
    control.sendTouch(phase: 0, x: 0.5, y: 0.5)   // down at screen centre
}

```

Inside a custom input handler, implement the dual-path strategy to support all iOS versions:

```swift
// Inside VPhoneVirtualMachineView.handleMouseEvent(...)
let normalized = normalizeCoordinate(localPoint)
if let ctrl = control, ctrl.useGuestTouchInjection {
    ctrl.sendTouch(phase: 1, x: normalized.x, y: normalized.y) // move
} else {
    // Fall back to native VZ touch
    let vzTouch = Dynamic._VZTouch(view: self,
                                   index: 0,
                                   phase: 1,
                                   location: normalized,
                                   swipeAim: nil,
                                   timestamp: timestamp)
    Dynamic(device).sendMultiTouchEvents([vzTouch.asObject!])
}

```

## Summary

- **`useGuestTouchInjection`** inspects the guest iOS major version parsed from `guestIOSVersion` and returns `true` only when the version is less than 26.
- **iOS < 26** uses the native Virtualization framework multitouch path via `VZMultiTouchEvent`.
- **iOS ≥ 26** (including iOS 18 and later) triggers guest-side injection through the `vphoned` daemon due to missing digitizer events in the 26.x kernel.
- The logic is implemented in [`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift) (lines 35‑44) and consumed by [`sources/vphone-cli/VPhoneVirtualMachineView.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneVirtualMachineView.swift) (lines 58‑66).

## Frequently Asked Questions

### What is the purpose of useGuestTouchInjection in vphone-cli?

The property acts as a version-gated switch that determines whether touch coordinates are injected into the guest VM via the native Apple Virtualization framework or forwarded to an internal HID daemon. It abstracts the complexity of kernel-level behavioral changes between iOS versions.

### At which iOS version does the touch path switch from native to guest-side injection?

The switch occurs at **iOS major version 26**. According to the source code in [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), any guest reporting a major version of 26 or higher will trigger `useGuestTouchInjection` to return `false`, forcing the system to use the guest-side `vphoned` injection path.

### What files contain the touch injection logic in the vphone-cli repository?

The core detection logic resides in **[`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift)** (lines 35‑44), while the routing implementation that respects this decision is located in **[`sources/vphone-cli/VPhoneVirtualMachineView.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneVirtualMachineView.swift)** (lines 58‑66). These two files handle the complete lifecycle of touch event delivery.

### Why does the Virtualization framework fail to deliver touch events on newer iOS versions?

On the **26.x kernel** used by iOS 18 and later, the USB-touchscreen driver extension receives raw HID data but no longer emits the digitizer events required by macOS to create `VZMultiTouchEvent` objects. Without these events, the native multitouch pipeline cannot propagate touches to the guest UI, necessitating the fallback to guest-side HID injection performed by `vphoned`.