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

2026 App ServicesDesignSwiftUI & UI Frameworks

WWDC26 · 28 min · App Services / Design / SwiftUI & UI Frameworks

What’s new in SwiftUI

Explore the latest additions to SwiftUI and discover how they can improve your apps. We’ll introduce a new Document protocol with direct disk access and snapshot-based diffing for building high-performance apps; new APIs for reordering content in lists, grids, and sections; and toolbar enhancements including visibility priority and auto-minimizing behavior. We’ll also cover expanded presentation APIs — including swipe actions on any view — plus AsyncImage caching improvements and lazy state initialization for Observable types.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 33 snippets

appearsActive environment value swift · at 3:20 ↗
struct SidebarFooterView: View {
    @Environment(\.appearsActive) private var appearsActive

    var body: some View {
        MyAccountView()
            .opacity(appearsActive ? 1 : 0.5)
    }
}
Menu icon visibility swift · at 3:34 ↗
CommandMenu("Stickers") {
    Button { openStore() } label: {
        Label("Store", systemImage: "bag.fill")
            .labelStyle(.titleAndIcon)
        }
    }
    // Other menu items
}
Prominent tab role swift · at 5:12 ↗
TabView {
    Tab { EventsTab() }
    Tab { HolidaysTab() }
    Tab { FunTab() }

    Tab(role: .prominent) {
        CartTab()
    }
}
Toolbar item visibility and overflow menu swift · at 6:15 ↗
// Toolbar item visibility priority

StickerPageView()
    .toolbar {
        ToolbarItemGroup {
            UndoButton()
            RedoButton()
        }
        .visibilityPriority(.high)
        ToolbarOverflowMenu {
            ChoosePhotoButton()
            ExportAsImageButton()
            ClearAllStickersButton()
        }
        ToolbarItem(placement: .topBarPinnedTrailing) {
            ShareButton()
        }
    }
Minimize toolbar on scroll with toolbarMinimizeBehavior swift · at 7:37 ↗
// Minimize toolbar when scrolling

ScrollView {
    StickerListView()
}
.toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)
Document creation sources with context parameter swift · at 9:47 ↗
// Use the context to create a document

@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 { /* ... */ }
    }
}
  
extension DocumentCreationSource {
    static let blank = Self(id: "blank")
    static let photo = Self(id: "photo")
}
Use the context to create a document swift · at 10:01 ↗
@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)
        }
    }
}
Document app declaration swift · at 10:43 ↗
@main
struct Stickers: App {
    var body: some Scene {
        DocumentGroup { /* ... */ }
        WindowGroup { /* ... */ }
    }
}
Implement document writing swift · at 11:25 ↗
@Observable
final class StickerDocument {
    // ...
}
Implement document writing: list writable formats swift · at 11:34 ↗
@Observable
final class StickerDocument {
    
    static let writableDocumentTypes: [UTType] = [.stickerDocument]
  
    // ...
}

import UniformTypeIdentifiers

extension UTType {
    static let stickerDocument = UTType(exportedAs: "stickerdocument")
}
Implement document writing: provide snapshot swift · at 11:45 ↗
@Observable
final class StickerDocument {
    
    static let writableDocumentTypes: [UTType] = [.stickerDocument]
  
    @MainActor
    func snapshot(contentType: UTType) async throws -> sending PageSnapshot { /* ... */ }
      
    // ...
}
Implement document writing: represent the snapshot swift · at 11:54 ↗
struct PageSnapshot {
    var background: Image
    var metadata: StickerPlacements
    var stickers: [Image]
}

struct StickerPlacements { /* ... */ }
Implement document writing: provide a DocumentWriter swift · at 12:13 ↗
@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)
    }
}
DocumentWriter: Snapshot swift · at 12:33 ↗
struct Writer<Snapshot>: DocumentWriter {
    typealias Snapshot = PageSnapshot
    
    // ...
}
DocumentWriter: PageSnapshot as Snapshot swift · at 12:36 ↗
struct Writer<Snapshot>: DocumentWriter {
    typealias Snapshot = PageSnapshot
  
    let contentType: UTType
    
    // ...
}
DocumentWriter protocol implementation swift · at 12:42 ↗
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 {
        // write .stickerDocument
    }
}
Progress reporting during writing swift · at 13:18 ↗
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 {
        // report progress…
        // write .stickerDocument
    }
}
Implement document reading with ReadableDocument protocol swift · at 13:27 ↗
extension StickerDocument: ReadableDocument {
  
}
Add PNG to supported formats list swift · at 14:35 ↗
@Observable
final class StickerDocument: WritableDocument {
  
    static let writableContentTypes: [UTType] = [.stickerDocument, .png]
}
Add content type checks swift · at 14:48 ↗
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 {
        if contentType.conforms(to: .stickerDocument) {
            // write .stickerDocument
        } else if contentType.conforms(to: .png)
        
    }
}
Writing multiple formats including PNG swift · at 14:56 ↗
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 {
        if contentType.conforms(to: .stickerDocument) {  
            // write .stickerDocument
        } else if contentType.conforms(to: .png) {
            let context = CGContext(/* ... */) 
            context.draw(/* ... */)
        }
    }
}
Reorderable list with reorderContainer swift · at 15:58 ↗
List {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
    }
    .reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
    difference.apply(to: &stickers)
}
Apply changes to a reorderable list's data source swift · at 16:14 ↗
import OrderedCollections // from https://github.com/apple/swift-collections

extension ReorderDifference where CollectionID == ReorderableSingleCollectionIdentifier {
    func apply(to values: inout [some Identifiable<ItemID>]) {
        var dictionary = OrderedDictionary(uniqueKeys: values.map { $0.id }, values: values)
        let destinationOffset: Int? = switch destination.position {
        case .before(let destination):
            dictionary.keys.firstIndex(of: destination)
        case .end:
            nil
        }
        dictionary.move(keys: sources, to: destinationOffset ?? values.endIndex)
        values = dictionary.values.elements
    }
}
Reorderable grid with LazyVGrid swift · at 16:48 ↗
LazyVGrid {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
    }
    .reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
    difference.apply(to: &stickers)
}
Swipe actions on List swift · at 18:12 ↗
List {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
            .swipeActions {
                DeleteButton(sticker: sticker)
            }
    }
}
Swipe actions on any view swift · at 18:15 ↗
ScrollView {
    LazyVStack {
        ForEach(stickers) { sticker in
            StickerListItemView(sticker: sticker)
                .swipeActions {
                    DeleteButton(sticker: sticker)
                }
        }
    }
}
.swipeActionsContainer()
Confirmation dialog with item binding swift · at 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 {
                        // ...
                    }
            }
        }
        .confirmationDialog(
            "Delete?", item: $stickerToDelete
        ) { sticker in
            DeleteStickerButton(sticker)
        }   
    }
}
Alert with item binding swift · at 19:35 ↗
struct StickerCanvasView: View {
    var stickers: [Sticker]
    @State private var stickerToDelete: Sticker?

    var body: some View {
        ZStack {
            ForEach(stickers) { sticker in
                PlacedStickerView(sticker: sticker)
                    .contextMenu {
                        // ...
                    }
            }
        }
        .alert(
            "Delete?", item: $stickerToDelete
        ) { sticker in
            DeleteStickerButton(sticker)
        }   
    }
}
AsyncImage with URLRequest and custom URLSession swift · at 21:18 ↗
@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)
@State converted to macro for lazy initialization swift · at 23:08 ↗
@Observable class StickerStore { }

struct StickerStoreView: View {
    // store is now lazily initialized, only
    // created once for the lifetime of the view
    @State private var store = StickerStore()

    var body: some View {
        // ...
    }
}
@State macro init assignment error swift · at 23:48 ↗
struct StickerPageView: View {
    @State private var page = StickerPage()
    let title: String
    
    init(title: String) {
        self.page = StickerPage(title: title) // Variable 'self.title' used before being initialized
        self.title = title
    }
    
    var body: some View {
        // ...
    }
}
Fixed @State macro init assignment error swift · at 24:02 ↗
struct StickerPageView: View {
    @State private var page: StickerPage // Removed default value to fix error
    let title: String
    
    init(title: String) {
        self.page = StickerPage(title: title)
        self.title = title
    }
    
    var body: some View {
        // ...
    }
}
@ContentBuilder swift · at 26:07 ↗
@ContentBuilder
func stickerLibraryView() -> some View {
  // ...
}

Resources