<!--
{
  "availability" : [
    "Xcode: 27.0.0 -",
    "visionOS: 27.0.0 -"
  ],
  "documentType" : "article",
  "framework" : "visionOS",
  "identifier" : "/documentation/visionOS/monitoring-fit-and-field-of-view-fidelity",
  "metadataVersion" : "0.1.0",
  "role" : "sampleCode",
  "title" : "Monitoring fit and field of view fidelity"
}
-->

# Monitoring fit and field of view fidelity

Respond to changes in fit and field of view fidelity on Apple Vision Pro by using the Visual Fidelity API.

## Overview

Some enterprise apps may need to verify an Apple Vision Pro wearer’s fit and field of view, often due to regulatory requirements.
Use the Visual Fidelity API in ARKit to monitor how well Apple Vision Pro fits the wearer and whether they experience the required field of view.
When fit accuracy or field of view coverage drifts out of range, the app can respond by coaching the wearer to adjust the fit or by making features that require accurate fit and field of view coverage unavailable.

The sample shows how to monitor visual fidelity and visualize the required field of view for use in your own app:

- In a shared space, the person configures monitoring, and the app displays live updates on fit accuracy and field of view coverage.
- In an immersive space, the app outlines the required field of view as a series of spheres along each eye’s boundary.

> Note: This sample requires an Apple Vision Pro. The Visual Fidelity API isn’t supported in the visionOS simulator.

## Configure the sample code project

Replace `Enterprise.license` with your license file.
The sample app requires a valid license file to monitor visual fidelity.

## Request the entitlement

Visual fidelity monitoring is part of enterprise APIs for visionOS, a collection of APIs that unlock capabilities for enterprise customers.
To use the Visual Fidelity API, you need to apply for the <doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.developer.arkit.visual-fidelity.allow> entitlement.
For more information, including how to apply for this entitlement, see <doc://com.apple.documentation/documentation/visionOS/building-spatial-experiences-for-business-apps-with-enterprise-apis>.

## Start monitoring visual fidelity

When you launch the sample, `ContentView` displays a Start Monitoring button, a Show Visualization button, and three controls bound to properties on `AppModel`:

- Field of View: A picker for `FieldOfViewSelection` with six options.
  The four <doc://com.apple.documentation/documentation/ARKit/VisualFidelityProvider/FieldOfView> presets (A, B, C, and D) cover standard regions.
  A custom polygon defined by 2D points in tangent-angle coordinates gives precise geometric control.
  Device Fit Only skips field of view monitoring when the app doesn’t need to specify a required field of view.
- Device Fit Updates: A toggle for `requestDeviceFitUpdates`.
  Turn it on when the app needs to react to fit status, usually to coach the wearer to adjust the fit of their Vision Pro or to turn off features that depend on a valid fit.
  Each <doc://com.apple.documentation/documentation/ARKit/VisualFidelityData> includes the wearer’s current <doc://com.apple.documentation/documentation/ARKit/VisualFidelityData/deviceFitStatus>.
  Turn it off when the app doesn’t need fit information.
  Then <doc://com.apple.documentation/documentation/ARKit/VisualFidelityData/deviceFitStatus> always reports `.valid`.
- Coaching Alerts: A toggle for `presentCoachingAlerts`.
  Turn it on to let the system handle coaching.
  The system shows its own notices when fit or coverage drifts out of range. For example, a notice titled “Adjust Fit” with guidance like, “Move Vision Pro slightly up”.
  The system dismisses the notice automatically when the condition resolves.
  Turn it off when the app presents its own coaching UI.

When the wearer taps Start Monitoring, `ToggleMonitoringButton` calls `AppModel.startMonitoring()`.
The `AppModel` captures the current selections, creates a <doc://com.apple.documentation/documentation/ARKit/VisualFidelityProvider> with them, and runs it on an <doc://com.apple.documentation/documentation/ARKit/ARKitSession>:

```swift
// AppModel.swift

let session = ARKitSession()
let provider = VisualFidelityProvider(
    fieldOfView: fieldOfView,
    requestDeviceFitUpdates: requestDeviceFitUpdates,
    presentCoachingAlerts: presentCoachingAlerts
)

try await session.run([provider])
```

With the session running, ARKit delivers a stream of fidelity data to `AppModel`.

## Respond to fidelity updates

When `AppModel` runs `monitor()`, it watches fidelity updates and stores the count, fit status, and field of view status that `StatusView` displays:

```swift
// AppModel.swift

group.addTask { [weak self, provider] in
    for await data in provider.fidelityDataUpdates {
        await self?.apply(data)
    }
}

@MainActor
private func apply(_ data: VisualFidelityData) {
    updateCount += 1
    deviceFitStatus = data.deviceFitStatus
    isFieldOfViewValid = data.isFieldOfViewValid
}
```

## Visualize the field of view

When the wearer taps the Show Visualization button, the app opens an immersive space where `ImmersiveView` outlines each eye’s field of view using a series of spheres, blue for the left eye, red for the right.
The button isn’t available when the wearer selects a custom polygon or device fit only, since ARKit only delivers <doc://com.apple.documentation/documentation/ARKit/FieldOfViewAnchor> updates for presets.

When `AppModel` runs `monitor()`, it watches anchor updates and stores each one in `currentFieldOfViewAnchor`:

```swift
// AppModel.swift

group.addTask { [weak self, provider] in
    for await update in provider.anchorUpdates {
        await self?.setAnchor(update.anchor)
    }
}

@MainActor
private func setAnchor(_ anchor: FieldOfViewAnchor) {
    currentFieldOfViewAnchor = anchor
}
```

When `currentFieldOfViewAnchor` changes, `ImmersiveView` positions a pool of sphere entities to match the anchor’s per-eye polygon points:

```swift
// ImmersiveView.swift

.onChange(of: appModel.currentFieldOfViewAnchor, initial: true) { _, anchor in
    guard let anchor else {
        leftContainer.children.removeAll()
        rightContainer.children.removeAll()
        return
    }

    let anchorSpace = anchor.coordinateSpace(correction: .rendered)
    guard let transform = try? root.transform(from: anchorSpace) else { return }

    sync(leftContainer, to: anchor.leftPolygonPoints, transform: transform, material: Self.leftEyeMaterial)
    sync(rightContainer, to: anchor.rightPolygonPoints, transform: transform, material: Self.rightEyeMaterial)
}
```

Use the visualization to compare presets and confirm which one covers the regions your app depends on.

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)