Skip to content

What Is New After WWDC 23 (SwiftUI)

A SwiftUI guide for coding agents. Also covers ios 17 swiftui features, observable macro migration, phaseanimator, withanimation completion, contentunavailableview, preview macro.

iOS 17 rebuilt SwiftUI data flow on the Observation framework and added phased animations, scroll-position control, and several quality-of-life views. This is the survey; deeper docs in this area cover each API.

Observation Replaces Combine

Mark a class with the @Observable macro and drop @Published: views track any property they read. @State now holds both value types and observable classes, a plain let replaces @ObservedObject, and @Environment(Store.self) with .environment(store) replaces the EnvironmentObject pair.

@Observable
final class Store {
    var products: [String] = []
    func fetch() async { /* load */ }
}

struct ProductsView: View {
    @State private var store = Store()

    var body: some View {
        List(store.products, id: \.self) { Text(verbatim: $0) }
            .task { if store.products.isEmpty { await store.fetch() } }
    }
}

@Bindable extracts a Binding from an observable type when a child needs write access.

Animation Completion and Phases

withAnimation gained a completion handler, and PhaseAnimator steps through a phase sequence with a different animation per phase. KeyframeAnimator covers keyframe timelines.

withAnimation {
    value.toggle()
} completion: {
    print("Animation finished")
}

PhaseAnimator(Phase.allCases, trigger: value) { phase in
    LoadingView().offset(x: phase.offset)
} animation: { phase in
    switch phase {
    case .start: .easeIn(duration: 0.3)
    case .loading: .easeInOut(duration: 0.5)
    case .finish: .easeOut(duration: 0.1)
    }
}

ScrollView Control

scrollPosition(id:) binds the first visible view's identity, in both directions: the user scrolls and the binding updates, or you set the binding and the view scrolls. It requires scrollTargetLayout on the content so SwiftUI knows where the identities live. scrollTargetBehavior(.paging) turns on paging.

ScrollView {
    ForEach(1..<100, id: \.self) { Text(verbatim: $0.formatted()) }
        .scrollTargetLayout()
}
.scrollPosition(id: $scrollPosition)

Smaller Additions

  • searchable gained isPresented for programmatic focus; searchScopes gained activation.
  • RotateGesture and MagnifyGesture track rotation and magnification.
  • ContentUnavailableView("Products list is empty", systemImage: "list.dash") is the standard empty state.
  • listRowSpacing and listSectionSpacing tune list spacing.
  • The #Preview macro declares previews for SwiftUI and UIKit in a few lines.
  • Swift Charts became scrollable and SF Symbols animatable.

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-what-is-new-after-wwdc23" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems