Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2026 Graphics & GamesSpatial Computing

WWDC26 · 15 min · Graphics & Games / Spatial Computing

Discover the Spatial Preview framework

Check out how the new Spatial Preview framework brings content from your Mac directly into visionOS. Discover how to build dynamic workflows with live-syncing and bidirectional editing across both platforms. Learn about the SpatialPreview API, device discovery, 2D and 3D session integration, and new Quick Look capabilities to elevate your Mac apps spatially.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 11 snippets

Document Preview Session with Device Picker swift · at 3:58 ↗
// Send and update documents using the Spatial Preview framework

import SwiftUI
import SpatialPreview
let deviceObserver = ConnectedSpatialEndpointObserver()

let previewSession = DocumentPreviewSession(name: "Immersive.aivu", contentType: .aivu)

func startPreview(contentURL: URL, endpoint: SpatialPreviewEndpoint) async throws {
    let endpoint = try await deviceObserver.endpoint
    try await previewSession.start(endpoint: endpoint)
    try await previewSession.updateContents(url: contentURL)
}

@State var showDevicePicker: Bool = false

var body: some View {
    ...
    .sheet(isPresented: $showDevicePicker) {
        SpatialPreviewDevicePicker(isPresented: $showDevicePicker) { endpoint in
            showDevicePicker = false
            Task {
                try await startPreview(filename: filename, endpoint: endpoint)
            }
        }
    }
}
Update Document Contents swift · at 5:20 ↗
// Send and update documents using the Spatial Preview framework

import SwiftUI
import SpatialPreview

ForEach(contentURLs, id: \.self) { url in
    Button {
        Task { try await previewSession?.updateContents(url: url) }
    }
}
.task(id: previewSession.map { ObjectIdentifier($0) }) {
    for await state in Observations({ session.state }) {
        if state.isInvalidated {
            previewSession = nil
            break
        }
    }
}

try await previewSession?.close()
Edit USD Live swift · at 7:36 ↗
// Edit USD live using USDKit and Spatial Preview

import SpatialPreview
import USDKit

let deviceObserver = ConnectedSpatialEndpointObserver()

var usdSession: USDPreviewSession?

func shareStage(to endpoint: SpatialPreviewEndpoint) async throws -> USDPreviewSession {
    let endpoint = try await deviceObserver.endpoint

    let stageURL = Bundle.main.url(forResource: "sampleScene", withExtension: "usdz")
    let stage = try USDStage.open(stageURL)
    usdSession = USDPreviewSession(stage: stage)

    try await usdSession?.start(endpoint: endpoint)
}
Opt out of optimization swift · at 8:56 ↗
// Optimization

import SpatialPreview




let endpoint = try await deviceObserver.endpoint
do {
    try await usdSession.start(endpoint: endpoint, parameters: .unmodified)
} catch USDPreviewSession.Error.assetUnshareable {
    // Handle Asset Unshareable error
}
USD Layout Variants swift · at 10:10 ↗
// LayoutVariants.usda
#usda 1.0
over "furniture" (
    variantSets = "Layout"
    variants = { string Layout = "LayoutA" }
)
{
    variantSet "Layout" = {
        "LayoutA" {
            // Default furniture position and rotation
        }
        "LayoutB" {
            // Moves furniture prims to a different position and rotation
        }
        ...
    }
}
Edit USD live using USDKit and Spatial Preview swift · at 10:17 ↗
// Edit USD live using USDKit and Spatial Preview

import SpatialPreview
import USDKit

func applyLayoutVariant(named layoutVariantName: String) throws {
    let prim = stage.prim(at: SdfPath("/root/furniture"))
    try prim.variantSets?.setSelection("Layout", variantName: layoutVariantName)
}
USD Stage Observations swift · at 10:49 ↗
// Edit USD live using Spatial Preview

import SpatialPreview
import USDKit

let observerToken: ObservationToken

observerToken = stage.addObserver(for: UsdStage.ObjectsDidChange.self) { notice in
    for path in notice.resyncedPaths {
        let prim = notice.stage.prim(at: path)
        guard prim.isValid else { continue }
        if prim.isAnnotation {
            // Handle annotation change
            break
        }
    }
}
Annotation Spec swift · at 11:13 ↗
// Annotation spec example

AppleTextAnnotation {
    // The textual representation of this annotation
    string text

    // The identifier for this specific author
    uniform string author

    // An identifier that is unique to your data tracking system
    uniform string identifier
}

/__documentAnnotationGroup__
Metadata for Object Manipulation swift · at 11:33 ↗
// Metadata required for object manipulation in Quick Look

customData = {
    dictionary apple = {
        bool spatialEditable = 1
    }
}
Session Options and Events swift · at 12:16 ↗
// Spatial Preview session options and events

import SpatialPreview
import USDKit

session.start(endpoint: endpoint, options: [.annotations, .perObjectManipulation, .export])

func listenForEvents(session: USDPreviewSession) async {
    for await event in session.events {
        if case .timeChanged(let time) = event {
            playbackModel.timeCode = time
        } else if case .playbackStateChanged(let isPlaying) = event {
            playbackModel.playbackStateChanged(isPlaying)
        }
    }
}
Observe Session Progress swift · at 12:38 ↗
// Observe Spatial Preview session progress

import SpatialPreview
import USDKit

@State private var sessionProgress: Double = 0

var body: some View {
    ...
    .task(id: usdSession.map { ObjectIdentifier($0) }) {
        guard let session = usdSession else { return }
        for await fraction in Observations({ session.progress.fractionCompleted }) {
            sessionProgress = fraction
        }
    }
    .overlay(alignment: .bottom) {
        ProgressView(value: sessionProgress)
            .padding()
    }
}

Resources