Highlight
SwiftUI in the 2027 platform releases introduces a new Document API with direct disk access and snapshot-based differential writes, reorderable containers, toolbar visibility priority and automatic minimization, swipe gestures for any view, plus compile-time improvements from the
@Statemacro andContentBuilder.
Core Content
Liquid Glass and Responding to Window State
In the 2027 releases, SwiftUI apps automatically receive the updated Liquid Glass appearance without any code changes. (02:17) Custom Liquid Glass elements on macOS can be marked as interactive, making them respond more smoothly to mouse clicks. On iPad, when an app window is inactive, icons and text automatically dim so users can identify the active window.
Developers can use the appearsActive environment value to precisely control how custom views appear when a window is inactive:
struct SidebarFooterView: View {
@Environment(\.appearsActive) private var appearsActive
var body: some View {
MyAccountView()
.opacity(appearsActive ? 1 : 0.5)
}
}
Key points:
appearsActiveis a new environment value in 2027- It returns
falsewhen the window is inactive andtruewhen it is active - Use it for visual feedback in custom sidebars, toolbars, and similar elements
On iPad and Mac, menu bars show only the icons for key actions by default. You can use .labelStyle(.titleAndIcon) to show both the icon and title for specific menu items, making them stand out more. (03:34)
Adaptive Toolbars and Visibility Control
As features grow, toolbars accumulate more buttons. On iPhone or in smaller windows, the system automatically hides buttons that do not fit. The 2027 release provides three new APIs to address this.
Visibility priority: Use visibilityPriority(.high) to keep important buttons visible first when space is constrained.
StickerPageView()
.toolbar {
ToolbarItemGroup {
UndoButton()
RedoButton()
}
.visibilityPriority(.high)
ToolbarOverflowMenu {
ChoosePhotoButton()
ExportAsImageButton()
ClearAllStickersButton()
}
ToolbarItem(placement: .topBarPinnedTrailing) {
ShareButton()
}
}
Key points:
visibilityPriority(.high)marks Undo and Redo as important actions and keeps them visible firstToolbarOverflowMenuplaces less frequently used buttons permanently in an overflow menu.topBarPinnedTrailingkeeps the Share button visible on the trailing side so it is never hidden
Automatic minimization: When the user scrolls down, the navigation bar automatically collapses to leave more room for content. (07:37)
ScrollView {
StickerListView()
}
.toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)
The New Document API
SwiftUI’s existing FileDocument and ReferenceFileDocument protocols are substantially expanded in the 2027 releases. The new API provides document creation context, optimized disk reads and writes, and direct access to a document URL. (08:12)
Document creation sources: An app can offer multiple ways to create a document at launch.
@main
struct Stickers: App {
var body: some Scene {
DocumentGroupLaunchScene("Create a Sticker Page") {
NewDocumentButton("New Sticker Page", source: .blank)
NewDocumentButton("Sticker Page from Photo…", source: .photo)
}
DocumentGroup { document in
StickerPageDocumentView(document)
} { configuration, context in
StickerPageDocument(configuration: configuration, context: context)
}
}
}
extension DocumentCreationSource {
static let blank = Self(id: "blank")
static let photo = Self(id: "photo")
}
Key points:
DocumentGroupLaunchScenedefines new-document buttons in the launch sceneDocumentCreationSourcedeclares different creation sources, such as blank or from a photo- The creation closure receives source information through the
contextparameter, letting the initializer decide what to do
Write optimization: The new WritableDocument protocol requires three pieces: writable formats, a snapshot method, and a writer. (11:25)
@Observable
final class StickerDocument {
static let writableDocumentTypes: [UTType] = [.stickerDocument]
@MainActor
func snapshot(contentType: UTType) async throws -> sending PageSnapshot {
makeSnapshot()
}
func writer(configuration: sending WriteConfiguration) -> sending Writer {
Writer(contentType: configuration.contentType)
}
}
struct Writer<Snapshot>: DocumentWriter {
typealias Snapshot = PageSnapshot
let contentType: UTType
nonisolated func write(
snapshot: sending PageSnapshot, to destination: URL,
previous: sending PageSnapshot?, progress: consuming Subprogress
) async throws {
// Compare the current and previous snapshots, then write only the changed parts.
// Use Subprogress to report write progress.
}
}
Key points:
snapshot()returns the document’s complete state at a moment in time, withsendingensuring thread safetyWriter’swritemethod isnonisolatedand asynchronous, so disk writes run in the background- Comparing
snapshotandpreviousenables incremental writes Subprogressreports save progress to the system
Multi-format export: A single writer can support multiple output formats. (14:35)
@Observable
final class StickerDocument: WritableDocument {
static let writableContentTypes: [UTType] = [.stickerDocument, .png]
}
// Dispatch based on contentType inside the write method.
if contentType.conforms(to: .stickerDocument) {
// Write the custom package format.
} else if contentType.conforms(to: .png) {
let context = CGContext(/* ... */)
context.draw(/* ... */)
}
Read protocol: ReadableDocument mirrors WritableDocument and uses DocumentReader to perform disk reads. (13:27)
Reorderable Containers
The 2027 release introduces the Reorderable API, which supports drag-based reordering in any container, including List and Grid. (15:58)
List {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
}
.reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
difference.apply(to: &stickers)
}
The same code can be used directly with LazyVGrid without changing the reordering logic:
LazyVGrid {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
}
.reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
difference.apply(to: &stickers)
}
Key points:
.reorderable()modifiesForEachto enable drag interaction.reorderContainer(for:)modifies the outer container and coordinates the reordering gesturedifference.applyusesOrderedDictionaryfrom swift-collections to commit ordering changes- watchOS is supported for the first time
Swipe Gestures on Any View
Swipe gestures are no longer limited to List; they can now be applied to any view. (18:15)
ScrollView {
LazyVStack {
ForEach(stickers) { sticker in
StickerListItemView(sticker: sticker)
.swipeActions {
DeleteButton(sticker: sticker)
}
}
}
}
.swipeActionsContainer()
Key points:
.swipeActions()modifies an individual list item.swipeActionsContainer()modifies the outerScrollViewand coordinates swipe gestures for all child items
Item Bindings for Confirmation Dialog and Alert
confirmationDialog and alert now support the same item-binding pattern as sheet. (18:54)
struct StickerCanvasView: View {
var stickers: [Sticker]
@State private var stickerToDelete: Sticker?
var body: some View {
ZStack {
ForEach(stickers) { sticker in
PlacedStickerView(sticker: sticker)
.contextMenu {
Button("Delete") { stickerToDelete = sticker }
}
}
}
.confirmationDialog("Delete?", item: $stickerToDelete) { sticker in
DeleteStickerButton(sticker)
}
}
}
Key points:
item: $stickerToDeletebinds an optional value- When the value is non-nil, the dialog appears automatically; when it is nil, the dialog disappears automatically
alertsupports the same item-binding pattern
Details
AsyncImage Cache Improvements
Previously, AsyncImage did not keep loaded images in memory. If you scrolled up and then back down, images were loaded again. In the 2027 release, standard HTTP caching is enabled by default without any code changes. (20:57)
For finer control, you can pass a custom URLRequest and URLSession:
@Observable class StickerStore {
static let imageSession: URLSession = {
let config = URLSessionConfiguration.default
config.urlCache = URLCache(
memoryCapacity: 64 * 1024 * 1024,
diskCapacity: 256 * 1024 * 1024)
return URLSession(configuration: config)
}()
}
ForEach(pets) { pet in
AsyncImage(request: URLRequest(
url: pet.imageURL,
cachePolicy: .returnCacheDataElseLoad)
)
}
.asyncImageURLSession(StickerStore.imageSession)
Key points:
URLRequestlets you set cache policy, headers, and other request details- The
asyncImageURLSessionmodifier sets a globalURLSession, which is useful for long-lived configuration - Default caching works automatically without extra code
@State as a Macro and Lazy Initialization
In the 2027 release, @State changes from a property wrapper into a macro. The biggest change is that Observable classes modified by @State now initialize only once. (23:08)
@Observable class StickerStore { }
struct StickerStoreView: View {
@State private var store = StickerStore()
var body: some View {
// store is created only during the first initialization. Parent view updates do not create it again.
}
}
This behavior has been back-deployed to iOS 17, macOS 14, and corresponding releases.
Migration note: If an @State variable has a default value and is also assigned in init, it produces a compile error. (23:48)
// Error: Variable 'self.title' used before being initialized
struct StickerPageView: View {
@State private var page = StickerPage()
let title: String
init(title: String) {
self.page = StickerPage(title: title)
self.title = title
}
}
// Fix: remove the default value.
struct StickerPageView: View {
@State private var page: StickerPage
let title: String
init(title: String) {
self.page = StickerPage(title: title)
self.title = title
}
}
ContentBuilder Compile-Time Improvements
Deeply nested SwiftUI views often trigger the compiler error: “The compiler is unable to type-check this expression in reasonable time.” (24:31)
The reason is that containers such as Section, Group, and ForEach each have multiple initializer overloads, and the compiler has to try all combinations before it can determine the type. In the 2027 release, these common builders are unified as ContentBuilder, eliminating most type-checking branches. (26:07)
@ContentBuilder
func stickerLibraryView() -> some View {
// ...
}
Key points:
ContentBuilderis the evolution ofViewBuilderand unifies several builder types- Compile-time improvements apply to all minimum deployment targets
- They take effect when building with Xcode 27, regardless of whether the target platform is 2027 or an earlier release
Key Takeaways
1. Refactor existing document apps with the new Document API
If you maintain an app based on FileDocument, migrating to the new WritableDocument/ReadableDocument protocols gives you incremental writes, asynchronous save progress, multi-format export, and more. The entry points are DocumentGroup and DocumentGroupLaunchScene.
2. Add adaptive toolbars to iPad and Mac apps
Use visibilityPriority(.high) for core actions, ToolbarOverflowMenu for secondary buttons, and .topBarPinnedTrailing to pin the Share button. Pair that with .toolbarMinimizeBehavior(.onScrollDown) to collapse the navigation bar during scrolling and improve the reading experience.
3. Replace custom drag reordering with the Reorderable API
If you previously used onMove or a custom DragGesture for list reordering, you can now replace it with .reorderable() plus .reorderContainer(). The code is simpler, animations and interactions are automatic, and it supports multiple container types such as List, Grid, and LazyVGrid.
4. Add swipe gestures to any scroll view
Swipe gestures are no longer limited to List. Any child view inside LazyVStack or ScrollView can add .swipeActions(). Add .swipeActionsContainer() to the outer container to coordinate the gestures.
5. Check compile errors caused by the @State macro change
After upgrading to Xcode 27, check whether any @State variables in your project are assigned again in init. The fix is to remove the default value from the property declaration and assign it only in init.
Related Sessions
- Modernize your UIKit app — A guide to screen-size adaptation when mixing UIKit and SwiftUI
- Build powerful drag and drop in SwiftUI — A deeper look at the Reorderable API and more drag-and-drop scenarios
- What’s new in SwiftData — Data persistence approaches that work with the Document API
- SwiftUI graphics and animations — New graphics and animation APIs that can be combined with visual effects in document apps
- What’s new in Xcode — How to use the SwiftUI Specialist Skill and What’s New In SwiftUI Skill in Xcode 27
Comments
GitHub Issues · utterances