iOS 18's SwiftUI release is heavier on developer-experience wins than iOS 26's — two macros that quietly remove real boilerplate, plus a handful of view-level additions (a rebuilt TabView, zoom transitions, scroll-phase observation, mesh gradients) that fill gaps teams had been working around with custom code for years.
#@Entry: environment, focused, and container values without the boilerplate
Before this macro, adding a custom EnvironmentValues key meant defining a private EnvironmentKey struct, a default value, and a computed property extension — three pieces of ceremony for one value. @Entry collapses all of it into one line.
extension EnvironmentValues {
@Entry var hapticsEnabled: Bool = true
}
The same macro works for FocusedValues and Transferable/ContainerValues entries. Use it every time you'd otherwise hand-write an EnvironmentKey — there's no reason left to write the old three-part version.
#@Previewable: inline state in #Preview
Previews that needed local @State used to require wrapping the previewed view in a throwaway container view just to hold the state. @Previewable lets you declare that state directly inside the #Preview closure.
#Preview {
@Previewable @State var isOn = false
Toggle("Enabled", isOn: $isOn)
}
Use this for any preview that needs to exercise interactive state — toggles, text fields, sheet presentation — without littering your view files with preview-only wrapper structs.
#Zoom navigation transitions
.navigationTransition(.zoom) animates a destination view growing out of the exact frame of the element that triggered navigation, paired with .matchedTransitionSource on the source.
@Namespace private var namespace
NavigationLink {
PhotoDetail(photo: photo)
.navigationTransition(.zoom(sourceID: photo.id, in: namespace))
} label: {
Thumbnail(photo: photo)
}
.matchedTransitionSource(id: photo.id, in: namespace)
This is the right default for any grid-to-detail navigation — photo grids, card lists, product catalogs — anywhere the old push transition felt disconnected from what the user tapped.
#Tab and TabSection: a rebuilt TabView
TabView moves from plain view-builder content to explicit Tab values, and gains .sidebarAdaptable, which turns the same tab set into a sidebar on iPad without a second layout to maintain.
TabView {
Tab("Home", systemImage: "house") {
HomeView()
}
Tab("Search", systemImage: "magnifyingglass") {
SearchView()
}
}
.tabViewStyle(.sidebarAdaptable)
If you're currently maintaining a NavigationSplitView on iPad and a TabView on iPhone as two separate code paths, this is worth migrating to directly.
#Scroll phase and geometry observation
.onScrollPhaseChange and .onScrollGeometryChange let you react to scroll state without owning a ScrollViewReader-driven offset tracker.
ScrollView {
content
}
.onScrollPhaseChange { oldPhase, newPhase in
if newPhase == .decelerating {
prefetchNextPage()
}
}
Reach for this instead of hand-rolled GeometryReader offset math any time you need "user is actively scrolling" or "user reached this threshold" — pagination triggers, sticky header state, and scroll-linked chrome all get simpler.
#MeshGradient
MeshGradient renders a gradient across a grid of control points with individual colors, rather than the two- or three-stop gradients LinearGradient and friends support.
MeshGradient(
width: 3,
height: 3,
points: [
[0, 0], [0.5, 0], [1, 0],
[0, 0.5], [0.5, 0.5], [1, 0.5],
[0, 1], [0.5, 1], [1, 1]
],
colors: [
.red, .purple, .indigo,
.orange, .white, .blue,
.yellow, .green, .mint
]
)
This replaces the custom Canvas- or shader-based gradient hacks teams built for marketing screens and onboarding backgrounds.
#What to adopt first
- Adopt
@Entryeverywhere you have a hand-writtenEnvironmentKey. It's a pure refactor, works at any deployment target your toolchain supports, and has zero runtime cost difference from the code it replaces. - Adopt
@Previewableimmediately — it only affects preview canvases, not your shipped deployment target, so there's no reason to wait. - Migrate iPad-specific split-view code to
TabViewwith.sidebarAdaptableif you're maintaining two navigation shells today. - Add zoom transitions to your highest-traffic grid-to-detail flow first — it's a visible, cheap win.
Deployment-target reality: @Entry and @Previewable are compile-time macros whose generated code doesn't require iOS 18 at runtime, so they're safe to use even if your deployment target is lower — check that the underlying EnvironmentValues/preview infrastructure you're touching doesn't itself require a newer OS. Zoom transitions, the new Tab API, scroll-phase observation, and MeshGradient are genuine iOS 18 runtime APIs; they need your deployment target at 18 or an if #available(iOS 18, *) fallback path.