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

2025 DesignSpatial ComputingSwiftUI & UI Frameworks

WWDC25 · 25 min · Design / Spatial Computing / SwiftUI & UI Frameworks

Set the scene with SwiftUI in visionOS

Discover exciting new APIs to enhance windows, volumes, and immersive spaces in your visionOS app. Fine tune the behavior of your scenes when relaunched or locked in place. Make volumes adapt to their surroundings with clipping margins and snapping. Stream immersive content from Mac to Vision Pro. Elevate your existing UIKit-based apps with volumes and immersive spaces.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 13 snippets

Disabling restoration swift · at 4:10 ↗
// Disabling restoration

WindowGroup("Tools", id: "tools") {
    ToolsView()
}
.restorationBehavior(.disabled)
Disabling restoration in UIKit swift · at 4:36 ↗
// Disabling restoration

windowScene.destructionConditions = [
    .systemDisconnection
]
Specifying launch window swift · at 5:02 ↗
// Specifying launch window

@AppStorage("isFirstLaunch") private var isFirstLaunch = true

var body: some Scene {
    WindowGroup("Stage Selection", id: "selection") {
        SelectionView()
    }

    WindowGroup("Welcome", id: "welcome") {
        WelcomeView()
            .onAppear {
                isFirstLaunch = false
            }
    }
    .defaultLaunchBehavior(isFirstLaunch ? .presented : .automatic)

    // ...
}
"suppressed" behavior swift · at 6:39 ↗
// "suppressed" behavior

WindowGroup("Tools", id: "tools") {
    ToolsView()
}
.restorationBehavior(.disabled)
.defaultLaunchBehavior(.suppressed)
Unique window swift · at 7:44 ↗
// Unique window

@AppStorage("isFirstLaunch") private var isFirstLaunch = true

var body: some Scene {
    // ...

    Window("Welcome", id: "welcome") {
        WelcomeView()
            .onAppear {
                isFirstLaunch = false
            }
    }
    .defaultLaunchBehavior(isFirstLaunch ? .presented : .automatic)

    WindowGroup("Main Stage", id: "main") {
        StageView()
    }

    // ...
}
Surface snapping swift · at 10:24 ↗
// Surface snapping

@Environment(\.surfaceSnappingInfo) private var snappingInfo
@State private var hidePlatform = false

var body: some View { 
    RealityView { /* ... */ }
    .onChange(of: snappingInfo) {
        if snappingInfo.isSnapped &&
            SurfaceSnappingInfo.authorizationStatus == .authorized
        {
            switch snappingInfo.classification {
                case .table:
                    hidePlatform = true
                default:
                    hidePlatform = false
            }
        }
    }
}
Clipping margins swift · at 14:41 ↗
// Clipping margins

@Environment(\.windowClippingMargins) private var windowMargins
@PhysicalMetric(from: .meters) private var pointsPerMeter = 1

var body: some View {
    RealityView { content in
        // ...
        waterfall = createWaterfallEntity()
        content.add(waterfall)
    } update: { content in
        waterfall.scale.y = Float(min(
            windowMargins.bottom / pointsPerMeter,
            maxWaterfallHeight))
        // ...
    }
    .preferredWindowClippingMargins(.bottom, maxWaterfallHeight * pointsPerMeter)
}
World recenter swift · at 16:44 ↗
// World recenter

var body: some View {
    RealityView { content in
        // ...
    }
    .onWorldRecenter {
        recomputePositions()
    }
}
Progressive immersion style swift · at 17:58 ↗
// Progressive immersion style

@State private var selectedStyle: ImmersionStyle = .progressive

var body: some Scene {
    ImmersiveSpace(id: "space") {
        ImmersiveView()
    }
    .immersionStyle(
        selection: $selectedStyle,
        in: .progressive(aspectRatio: .portrait))
}
Mixed immersion style swift · at 18:37 ↗
// Mixed immersion style

@State private var selectedStyle: ImmersionStyle = .progressive

var body: some Scene {
    ImmersiveSpace(id: "space") {
        ImmersiveView()
    }
    .immersionStyle(selection: $selectedStyle, in: .mixed)
    .immersiveEnvironmentBehavior(.coexist)
}
Remote immersive space swift · at 20:14 ↗
// Remote immersive space

// Presented on visionOS
RemoteImmersiveSpace(id: "preview-space") {
    CompositorLayer(configuration: config) { /* ... */ }
}

// Presented on macOS
WindowGroup("Main Stage", id: "main") {
    StageView()
}
'CompositorLayer' is a 'CompositorContent' swift · at 20:48 ↗
// 'CompositorLayer' is a 'CompositorContent'

struct ImmersiveContent: CompositorContent {
    @Environment(\.scenePhase) private var scenePhase

    var body: some CompositorContent {
        CompositorLayer { renderer in
            // ...
        }
        .onImmersionChange { oldImmersion, newImmersion in
            // ...
        }
    }
}
Scene bridging swift · at 23:00 ↗
// Scene bridging

import UIKit
import SwiftUI

// Declare the scenes
class MyHostingSceneDelegate: NSObject, UIHostingSceneDelegate {
    static var rootScene: some Scene {
        WindowGroup(id: "my-volume") {
            ContentView()
        }
        .windowStyle(.volumetric)
    }
}

// Create a request for the scene
let requestWithId = UISceneSessionActivationRequest(
    hostingDelegateClass: MyHostingSceneDelegate.self, id: "my-volume")!

// Send a request
UIApplication.shared.activateSceneSession(for: requestWithId)

Resources