Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Reviews, improves, and writes SwiftUI code following state management, view composition, performance, and iOS 26+ Liquid Glass best practices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/latest-apis.md
1# Latest SwiftUI APIs Reference23> Based on a comparison of Apple's documentation using the Sosumi MCP, we found the latest recommended APIs to use.45## Table of Contents6- [Always Use (iOS 15+)](#always-use-ios-15)7- [When Targeting iOS 16+](#when-targeting-ios-16)8- [When Targeting iOS 17+](#when-targeting-ios-17)9- [When Targeting iOS 18+](#when-targeting-ios-18)10- [When Targeting iOS 26+](#when-targeting-ios-26)1112---1314## Always Use (iOS 15+)1516These APIs have been deprecated long enough that there is no reason to use the old variants.1718### Compact Replacements1920These replacements have minimal API shape changes. Most are near-direct swaps; a few require an additional parameter or structural adjustment:2122- **`navigationTitle(_:)`** instead of `navigationBarTitle(_:)`23- **`toolbar { ToolbarItem(...) }`** instead of `navigationBarItems(...)` (structural change)24- **`toolbarVisibility(.hidden, for: .navigationBar)`** instead of `navigationBarHidden(_:)`25- **`statusBarHidden(_:)`** instead of `statusBar(hidden:)`26- **`ignoresSafeArea(_:edges:)`** instead of `edgesIgnoringSafeArea(_:)`27- **`preferredColorScheme(_:)`** instead of `colorScheme(_:)`28- **`foregroundStyle(_:)`** instead of `foregroundColor(_:)` (e.g., `.foregroundStyle(.primary)`)29- **`clipShape(.rect(cornerRadius:))`** instead of `cornerRadius()`30- **`textInputAutocapitalization(_:)`** instead of `autocapitalization(_:)` (note: `.never` replaces `.none`)31- **`animation(_:value:)`** instead of `animation(_:)` (adds required `value:` parameter; back-deploys to iOS 13+)3233### Presentation3435- **Always use `.confirmationDialog(_:isPresented:actions:message:)`** instead of `actionSheet(...)`.36- **Always use `.alert(_:isPresented:actions:message:)`** instead of `alert(isPresented:content:)`.3738Both take a title `String`, `isPresented: Binding<Bool>`, an `actions` builder with `Button` items (supporting `role: .destructive` / `.cancel`), and an optional `message` builder:3940```swift41.alert("Delete Item?", isPresented: $showAlert) {42Button("Delete", role: .destructive) { deleteItem() }43Button("Cancel", role: .cancel) { }44} message: {45Text("This action cannot be undone.")46}47```4849### Text Input5051**Always use `onSubmit(of:_:)` and `focused(_:equals:)` instead of `TextField` `onEditingChanged`/`onCommit` callbacks.**5253```swift54@FocusState private var isFocused: Bool5556TextField("Search", text: $query)57.focused($isFocused)58.onSubmit { performSearch() }59```6061### Accessibility6263**Always use dedicated accessibility modifiers instead of the generic `accessibility(...)` variants.** Use `.accessibilityLabel()`, `.accessibilityValue()`, `.accessibilityHint()`, `.accessibilityAddTraits()`, `.accessibilityHidden()` instead of `.accessibility(label:)`, `.accessibility(value:)`, etc.6465### Custom Environment / Container Values6667**Always use the `@Entry` macro instead of manual `EnvironmentKey` conformance.** The `@Entry` macro was introduced in Xcode 16 and back-deploys to all OS versions.6869```swift70// Modern — one line replaces ~10 lines of EnvironmentKey boilerplate71extension EnvironmentValues {72@Entry var myCustomValue: String = "Default value"73}74```7576### Styling7778**Always use `Button` instead of `onTapGesture()` unless you need tap location or count.**7980```swift81Button("Tap me") { performAction() }8283// Use onTapGesture only when you need location or count84Image("photo")85.onTapGesture(count: 2) { handleDoubleTap() }86```8788---8990## When Targeting iOS 16+9192### Navigation9394**Use `NavigationStack` (or `NavigationSplitView`) instead of `NavigationView`.** Value-based `NavigationLink(value:)` with `.navigationDestination(for:)` replaces destination-based links.9596```swift97NavigationStack {98List(items) { item in99NavigationLink(value: item) { Text(item.name) }100}101.navigationDestination(for: Item.self) { DetailView(item: $0) }102}103```104105### Simple Renames106107- **`tint(_:)`** instead of `accentColor(_:)`108- **`autocorrectionDisabled(_:)`** instead of `disableAutocorrection(_:)`109110### Clipboard111112**Prefer `PasteButton` for user-initiated paste UI** to avoid paste prompts. It handles permissions automatically. Use `UIPasteboard` only when you need programmatic or non-`Transferable` clipboard access (triggers the paste permission prompt).113114```swift115PasteButton(payloadType: String.self) { strings in116pastedText = strings.first ?? ""117}118```119120---121122## When Targeting iOS 17+123124### State Management125126- **Prefer `@Observable` over `ObservableObject` for new code.** Use `@State` instead of `@StateObject`; use `@Bindable` instead of `@ObservedObject`. See `state-management.md` for full `@Observable` migration patterns.127128### Events129130**Use `onChange(of:initial:_:)` or `onChange(of:) { }` instead of `onChange(of:perform:)`.**131132The deprecated variant passes only the new value. The modern variants provide either both old and new values, or a no-parameter closure.133134- **No-parameter** (most common): `.onChange(of: value) { doSomething() }`135- **Old and new values**: `.onChange(of: value) { old, new in ... }`136- **With initial trigger**: `.onChange(of: value, initial: true) { ... }`137- **Deprecated**: `.onChange(of: value) { newValue in ... }` — single-parameter closure138139### Sensory Feedback140141**Prefer `sensoryFeedback(_:trigger:)` and related overloads instead of `UIImpactFeedbackGenerator`, `UISelectionFeedbackGenerator`, and `UINotificationFeedbackGenerator` in SwiftUI views.**142143Attach haptics declaratively to the view that owns the state change, rather than imperatively firing UIKit generators inside button actions.144145```swift146@State private var isFavorite = false147148Button("Favorite", systemImage: isFavorite ? "heart.fill" : "heart") {149isFavorite.toggle()150}151.sensoryFeedback(.selection, trigger: isFavorite)152```153154Use the conditional overload when feedback should fire only for specific transitions:155156```swift157.sensoryFeedback(.selection, trigger: phase) { old, new in158old == .inactive || new == .expanded159}160```161162### Gestures163164- **`MagnifyGesture`** instead of `MagnificationGesture` (access magnitude via `value.magnification`)165- **`RotateGesture`** instead of `RotationGesture` (access angle via `value.rotation`)166167### Layout168169**Consider `containerRelativeFrame()` or `visualEffect()` as alternatives to `GeometryReader` for sizing and position-based effects.** `GeometryReader` is not deprecated and remains necessary for many measurement-based layouts.170171```swift172Image("hero")173.resizable()174.containerRelativeFrame(.horizontal) { length, axis in length * 0.8 }175```176177- **`visualEffect { content, geometry in ... }`** — position-based effects (parallax, offsets) without a `GeometryReader` wrapper.178- **`onGeometryChange(for:of:action:)`** — react to geometry changes of a specific view; useful for driving state/effects. `GeometryReader` is still better when layout itself depends on geometry. Note the two-closure shape:179```swift180.onGeometryChange(for: CGFloat.self) { proxy in proxy.size.height } action: { newHeight in height = newHeight }181```182- **`.coordinateSpace(.named("scroll"))`** instead of `.coordinateSpace(name: "scroll")`.183184---185186## When Targeting iOS 18+187188### Tabs189190**Use the `Tab` API instead of `tabItem(_:)`.**191192```swift193TabView {194Tab("Home", systemImage: "house") { HomeView() }195Tab("Search", systemImage: "magnifyingglass") { SearchView() }196Tab("Profile", systemImage: "person") { ProfileView() }197}198```199200When using `Tab(role:)`, all tabs must use the `Tab` syntax. Mixing `Tab(role:)` with `.tabItem()` causes compilation errors.201202### Previews203204**Use `@Previewable` for dynamic properties in previews.**205206```swift207// Modern (iOS 18+)208#Preview {209@Previewable @State var isOn = false210Toggle("Setting", isOn: $isOn)211}212```213214---215216## When Targeting iOS 26+217218For Liquid Glass APIs (`glassEffect`, `GlassEffectContainer`, glass button styles), see [liquid-glass.md](liquid-glass.md).219220### Scroll Edge Effects221222**Use `scrollEdgeEffectStyle(_:for:)` to configure scroll edge behavior.**223224```swift225ScrollView {226// content227}228.scrollEdgeEffectStyle(.soft, for: .top)229```230231### Background Extension232233**Use `backgroundExtensionEffect()` for edge-extending blurred backgrounds.**234235Views behind a Liquid Glass sidebar can appear clipped. This modifier mirrors and blurs content outside the safe area so artwork remains visible.236237```swift238Image("hero")239.backgroundExtensionEffect()240```241242> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)243244### Tab Bar245246**Use `tabBarMinimizeBehavior(_:)` to control tab bar minimization on scroll.**247248```swift249TabView {250// tabs251}252.tabBarMinimizeBehavior(.onScrollDown)253```254255**Use `tabViewBottomAccessory` for persistent controls above the tab bar.** Read `tabViewBottomAccessoryPlacement` from the environment to adapt content when the accessory collapses into the tab bar area.256257```swift258TabView {259// tabs260}261.tabViewBottomAccessory {262NowPlayingBar()263}264```265266**Use `Tab(role: .search)` for a dedicated search tab.** The tab separates from the rest and morphs into a search field when selected.267268```swift269TabView {270Tab("Home", systemImage: "house") { HomeView() }271Tab("Profile", systemImage: "person") { ProfileView() }272Tab(role: .search) { SearchResultsView() }273}274```275276> Source: "What's new in SwiftUI" (WWDC25, session 256) and "Build a SwiftUI app with the new design" (WWDC25, session 323)277278### Toolbars279280**Use `ToolbarSpacer` to control grouping of toolbar items.** Fixed spacers visually separate related groups; flexible spacers push items apart.281282```swift283.toolbar {284ToolbarItem(placement: .topBarTrailing) {285Button("Up", systemImage: "chevron.up") { }286}287ToolbarItem(placement: .topBarTrailing) {288Button("Down", systemImage: "chevron.down") { }289}290ToolbarSpacer(.fixed)291ToolbarItem(placement: .topBarTrailing) {292Button("Settings", systemImage: "gear") { }293}294}295```296297**Use `sharedBackgroundVisibility(.hidden)` to remove the glass group background from an individual toolbar item.**298299```swift300ToolbarItem(placement: .topBarTrailing) {301Image(systemName: "person.circle.fill")302.sharedBackgroundVisibility(.hidden)303}304```305306**Use `badge(_:)` on toolbar item content to display an indicator.**307308```swift309ToolbarItem(placement: .topBarTrailing) {310Button("Notifications", systemImage: "bell") { }311.badge(unreadCount)312}313```314315> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)316317### Search318319**Use `searchToolbarBehavior(.minimizable)` to opt into a minimized search button.** The system may automatically minimize search into a toolbar button depending on available space. Use this modifier to explicitly opt in.320321```swift322NavigationStack {323ContentView()324.searchable(text: $query)325.searchToolbarBehavior(.minimizable)326}327```328329> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)330331### Animations332333**Use `@Animatable` macro instead of manual `animatableData` declarations.** The macro auto-synthesizes `animatableData` from all animatable properties. Use `@AnimatableIgnored` to exclude specific properties.334335```swift336@Animatable337struct Wedge: Shape {338var startAngle: Angle339var endAngle: Angle340@AnimatableIgnored var drawClockwise: Bool341342func path(in rect: CGRect) -> Path { /* ... */ }343}344```345346> Source: "What's new in SwiftUI" (WWDC25, session 256)347348### Presentations349350**Use `navigationZoomTransition` to morph sheets out of their source view.** Toolbar items and buttons can serve as the transition source.351352```swift353.toolbar {354ToolbarItem {355Button("Add", systemImage: "plus") { showSheet = true }356.navigationTransitionSource(id: "addSheet", namespace: namespace)357}358}359.sheet(isPresented: $showSheet) {360AddItemView()361.navigationTransitionDestination(id: "addSheet", namespace: namespace)362}363```364365> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)366367### Controls368369**Use `controlSize(.extraLarge)` for extra-large prominent action buttons.**370371```swift372Button("Get Started") { }373.buttonStyle(.borderedProminent)374.controlSize(.extraLarge)375```376377**Use `concentric` corner style for buttons that match their container's corners.**378379```swift380Button("Confirm") { }381.clipShape(.rect(cornerRadius: 12, style: .concentric))382```383384**Sliders now support tick marks and a neutral value.**385386```swift387Slider(value: $speed, in: 0.5...2.0, step: 0.25) {388Text("Speed")389} ticks: {390SliderTick(value: 0.6)391SliderTick(value: 0.9)392}393.sliderNeutralValue(1.0)394```395396> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)397398### Rich Text399400**Use `TextEditor` with an `AttributedString` binding for rich text editing.** Supports bold, italic, underline, strikethrough, custom fonts, foreground/background colors, paragraph styles, and Genmoji.401402```swift403@State private var text: AttributedString = "Hello, world!"404405var body: some View {406TextEditor(text: $text)407}408```409410> Source: "Cook up a rich text experience in SwiftUI with AttributedString" (WWDC25, session 280)411412### Web Content413414**Use `WebView` to display web content.** For richer interaction, create a `WebPage` observable model.415416```swift417// Simple URL display418WebView(url: URL(string: "https://example.com")!)419420// With observable model421@State private var page = WebPage()422423WebView(page)424.onAppear { page.load(URLRequest(url: myURL)) }425.navigationTitle(page.title ?? "")426```427428> Source: "Meet WebKit for SwiftUI" (WWDC25, session 231)429430### Drag and Drop431432**Use `dragContainer` for multi-item drag operations.** Combine with `DragConfiguration` for custom drag behavior and `onDragSessionUpdated` to observe events.433434```swift435PhotoGrid(photos: photos)436.dragContainer(for: Photo.self) { selection in437return selection.map { $0.transferable }438}439.onDragSessionUpdated { session in440if session.phase == .endedWithDelete {441deleteSelectedPhotos()442}443}444```445446> Source: "What's new in SwiftUI" (WWDC25, session 256)447448### Scene Bridging449450**UIKit and AppKit lifecycle apps can now request SwiftUI scenes.** This enables using SwiftUI-only scene types like `MenuBarExtra` and `ImmersiveSpace` from imperative lifecycle apps via `UIApplication.shared.activateSceneSession(for:errorHandler:)`.451452> Source: "What's new in SwiftUI" (WWDC25, session 256)453454---455456## Quick Lookup Table457458| Deprecated | Recommended | Since |459|-----------|-------------|-------|460| `navigationBarTitle(_:)` | `navigationTitle(_:)` | iOS 15+ |461| `navigationBarItems(...)` | `toolbar { ToolbarItem(...) }` | iOS 15+ |462| `navigationBarHidden(_:)` | `toolbarVisibility(.hidden, for: .navigationBar)` | iOS 15+ |463| `statusBar(hidden:)` | `statusBarHidden(_:)` | iOS 15+ |464| `edgesIgnoringSafeArea(_:)` | `ignoresSafeArea(_:edges:)` | iOS 15+ |465| `colorScheme(_:)` | `preferredColorScheme(_:)` | iOS 15+ |466| `foregroundColor(_:)` | `foregroundStyle(_:)` | iOS 15+ |467| `cornerRadius(_:)` | `clipShape(.rect(cornerRadius:))` | iOS 15+ |468| `actionSheet(...)` | `confirmationDialog(...)` | iOS 15+ |469| `alert(isPresented:content:)` | `alert(_:isPresented:actions:message:)` | iOS 15+ |470| `autocapitalization(_:)` | `textInputAutocapitalization(_:)` | iOS 15+ |471| `accessibility(label:)` etc. | `accessibilityLabel()` etc. | iOS 15+ |472| `TextField` `onCommit`/`onEditingChanged` | `onSubmit` + `focused` | iOS 15+ |473| `animation(_:)` (no value) | `animation(_:value:)` | Back-deploys (iOS 13+) |474| Manual `EnvironmentKey` | `@Entry` macro | Back-deploys (Xcode 16+) |475| `NavigationView` | `NavigationStack` / `NavigationSplitView` | iOS 16+ |476| `accentColor(_:)` | `tint(_:)` | iOS 16+ |477| `disableAutocorrection(_:)` | `autocorrectionDisabled(_:)` | iOS 16+ |478| `UIPasteboard.general` | `PasteButton` | iOS 16+ |479| `onChange(of:perform:)` | `onChange(of:) { }` or `onChange(of:) { old, new in }` | iOS 17+ |480| `UIImpactFeedbackGenerator` / `UISelectionFeedbackGenerator` / `UINotificationFeedbackGenerator` | `sensoryFeedback(_:trigger:)` | iOS 17+ |481| `MagnificationGesture` | `MagnifyGesture` | iOS 17+ |482| `RotationGesture` | `RotateGesture` | iOS 17+ |483| `coordinateSpace(name:)` | `coordinateSpace(.named(...))` | iOS 17+ |484| `ObservableObject` | `@Observable` | iOS 17+ |485| `tabItem(_:)` | `Tab` API | iOS 18+ |486| Manual `animatableData` | `@Animatable` macro | iOS 26+ |487| `presentationBackground(_:)` on sheets | Default Liquid Glass sheet material | iOS 26+ |488| Custom toolbar background hacks | `scrollEdgeEffectStyle(_:for:)` | iOS 26+ |489