iOS 18's SwiftUI release is heavier on developer-experience wins than iOS 26's. Two macros quietly delete real boilerplate. The rest is view-level: a rebuilt TabView, zoom transitions, scroll-phase observation, mesh gradients. All of it fills gaps teams had been working around with custom code for years.
#@Entry: environment, focused, and container values without the boilerplate
One custom EnvironmentValues key used to cost three pieces: a private EnvironmentKey struct, a default value, and a computed property extension. @Entry is one line.
extension EnvironmentValues {
@Entry var hapticsEnabled: Bool = true
}
The same macro covers FocusedValues and Transferable/ContainerValues entries. Use it anywhere you'd hand-write an EnvironmentKey. Nothing is left that justifies the old three-part version.
#@Previewable: inline state in #Preview
A preview that needed local @State used to need a throwaway container view built for nothing but holding it. @Previewable declares that state right inside the #Preview closure.
#Preview {
@Previewable @State var isOn = false
Toggle("Enabled", isOn: $isOn)
}
Toggles, text fields, any preview that exercises interactive state. The preview-only wrapper structs stop cluttering your view files.
#Zoom navigation transitions
Pair .navigationTransition(.zoom) on the destination with .matchedTransitionSource on the source. The destination then grows out of the exact frame of the element that triggered navigation.
@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)
Make this the default for grid-to-detail navigation. Photo grids and product catalogs. Anywhere the old push transition felt disconnected from what the user tapped.
#Tab and TabSection: a rebuilt TabView
TabView content stops being a plain view builder and becomes explicit Tab values. .sidebarAdaptable turns that same tab set into a sidebar on iPad. One layout to maintain, not two.
TabView {
Tab("Home", systemImage: "house") {
HomeView()
}
Tab("Search", systemImage: "magnifyingglass") {
SearchView()
}
}
.tabViewStyle(.sidebarAdaptable)
If you're carrying a NavigationSplitView on iPad and a TabView on iPhone as two separate code paths, migrate straight to this.
#Scroll phase and geometry observation
.onScrollPhaseChange and .onScrollGeometryChange report scroll state directly. No ScrollViewReader-driven offset tracker to own.
ScrollView {
content
}
.onScrollPhaseChange { oldPhase, newPhase in
if newPhase == .decelerating {
prefetchNextPage()
}
}
Any time you need "user is actively scrolling" or "user reached this threshold", these replace hand-rolled GeometryReader offset math. Pagination triggers get simpler. So do sticky header state and scroll-linked chrome.
#MeshGradient
MeshGradient spreads color across a grid of control points, each point carrying its own color. LinearGradient and friends stop at two or three stops.
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
]
)
That kills the custom Canvas- and shader-based gradient hacks teams built for marketing screens and onboarding backgrounds.
#What to adopt first
- Replace every hand-written
EnvironmentKeywith@Entry. Pure refactor. It works at any deployment target your toolchain supports, and carries zero runtime cost difference from the code it replaces. - Take
@Previewablenow. It touches preview canvases only, never your shipped deployment target. - If you keep two navigation shells today, move the iPad split-view code onto
TabViewwith.sidebarAdaptable. - Add zoom transitions to your highest-traffic grid-to-detail flow first. Cheap, and users see it.
@Entry and @Previewable are compile-time macros whose generated code doesn't require iOS 18 at runtime, so they're safe even with a lower deployment target. 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. Those need your deployment target at 18 or an if #available(iOS 18, *) fallback path.