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
searchablegainedisPresentedfor programmatic focus;searchScopesgainedactivation.RotateGestureandMagnifyGesturetrack rotation and magnification.ContentUnavailableView("Products list is empty", systemImage: "list.dash")is the standard empty state.listRowSpacingandlistSectionSpacingtune list spacing.- The
#Previewmacro declares previews for SwiftUI and UIKit in a few lines. - Swift Charts became scrollable and SF Symbols animatable.