SwiftUI - visionOS 27
What's New in SwiftUI for visionOS 27
The visionOS SwiftUI story is mostly the shared OS 27 work applied to spatial apps: document APIs for project packages, reorderable palettes, drag containers, cached images, and lazy State in windowed controls.
visionOS focus
Use the new document reader/writer split for project packages and generated previews.
Reordering and dragApply the new container APIs to palettes and layer lists, not every spatial object.
State and imagesCached images and lazy State help repeatedly opened tool windows stay light.
Document Packages Fit Spatial Projects
SwiftUI's new document protocols are useful on visionOS when a project is a package: scene metadata, thumbnails, annotations, imported assets, and generated previews can be read and written asynchronously.
The same split from the other platforms applies here. A snapshot represents the project state at save time, while the writer handles the actual files on disk. That keeps a spatial editor responsive while previews, thumbnails, or asset sidecars are written.
struct SpatialProjectWriter: DocumentWriter {
typealias Snapshot = SpatialProjectSnapshot
nonisolated func write(
snapshot: sending SpatialProjectSnapshot,
to destination: URL,
previous: sending SpatialProjectSnapshot?,
progress: consuming Subprogress
) async throws {
try await writeChangedProjectFiles(snapshot, previous, to: destination)
}
}
Reordering and Drag Need Spatial Restraint
SwiftUI's new reordering and drag container APIs are available on platforms that support drag and drop, with the drag container modifier called out for iOS, iPadOS, and visionOS 27. On visionOS, use them for clear object lists and tool palettes, not for every spatial object in the scene.
The useful visionOS pattern is still a bounded SwiftUI surface: a layer list, asset tray, material palette, or inspector. Let the list or grid own the reorder operation and keep the immersive content focused on previewing the result.
ForEach(layers) { layer in
LayerRow(layer)
}
.reorderable()
.dragContainer(for: Layer.ID.self) { id in
transferItems(for: id)
}
State and Images Stay Lightweight
Reopened tool windows and inspectors benefit from the shared runtime changes. AsyncImage respects
HTTP caching, and classes stored in @State are initialized lazily instead of being created during
every parent view update.
That combination is useful for spatial apps because the same panels are often opened, dismissed, and reopened. Cached thumbnails avoid repeated network work, while lazy State prevents a tool model from being created until the panel actually participates in the view lifetime.
struct AssetPreview: View {
let url: URL
@State private var model = PreviewModel()
var body: some View {
AsyncImage(url: url)
.task { await model.preparePreview() }
}
}