Highlight
Franck uses the Landmarks sample project to demonstrate rebuilding a SwiftUI app under the Xcode 26 SDK, covering changes across five areas: App structure, Toolbars, Search, Controls, and custom Liquid Glass.
Core Content
Each year a new SDK drops, developers fear the same thing: “it looks better, but I have to rewrite all my code.” This time iOS 26 / macOS Tahoe introduces Liquid Glass, and the first reaction is similar: the sidebar is glass, the tab bar floats, buttons became capsule-shaped—do I need to rewrite all my existing toolbars, sheets, and buttons?
Franck’s answer: recompile with Xcode 26 first, see what you get for free, then decide whether to write code. He walks through a real Landmarks sample app—the hero image is cropped by the sidebar, five toolbar buttons have no visual hierarchy, the search bar placement differs between iPhone and iPad. Under the old APIs, these problems required hand-written adaptation code. The new SDK turns them into one or two modifiers: backgroundExtensionEffect extends the image beyond the safe area automatically, ToolbarSpacer groups toolbar items by semantics, searchable on NavigationSplitView adapts to the iPhone bottom and iPad top trailing automatically. The demo logic is clear: eat the automatic improvements first, write new APIs only for the semantics you need to express.
Detail
App structure: sidebar defaults to Liquid Glass, floating above content. If the sidebar crops the hero image, use backgroundExtensionEffect to extend the image reflection outside the safe area (03:59).
// Extend background images
struct LandmarkDetailView: View {
let landmark: Landmark
var body: some View {
ScrollView {
VStack {
Image(landmark.backgroundImageName)
.resizable()
.aspectRatio(contentMode: .fill)
.backgroundExtensionEffect()
}
}
}
}
Key points:
.backgroundExtensionEffect()mirrors and blurs the image outside the safe area, while the original content remains visible inside.- No need to manually calculate sidebar width or re-crop images—the reflection area expands automatically when the sidebar hides.
TabView’s tab bar becomes floating on iPhone, can minimize on scroll, and supports an accessory above it (like Music’s playback bar) (05:39).
// Tab bar accessory
TabView {
// tabs
}
.tabBarMinimizeBehavior(.onScrollDown)
.tabViewBottomAccessory {
MusicPlaybackView()
}
struct MusicPlaybackView: View {
@Environment(\.tabViewBottomAccessoryPlacement)
var placement
var body: some View {
if placement == .inline {
// compact layout
} else {
// full layout
}
}
}
Key points:
tabBarMinimizeBehavior(.onScrollDown)minimizes the tab bar on scroll down, restores on scroll up.tabViewBottomAccessoryplaces a persistent view above the tab bar, using the space freed when minimized.- Use the environment value
tabViewBottomAccessoryPlacementto distinguish between.inline(embedded when minimized) and expanded layouts.
Toolbars: items group automatically, use ToolbarSpacer to explicitly control semantic boundaries (08:26).
// Visually separate toolbar items
struct LandmarkDetailView: View {
var body: some View {
ScrollView {
ScrollContent()
}
.toolbar {
ToolbarItem { ShareLink() }
ToolbarSpacer(.fixed)
ToolbarItem { FavoriteButton() }
ToolbarItem { CollectionsButton() }
ToolbarSpacer(.fixed)
ToolbarItem { InspectorToggle() }
}
}
}
Key points:
- Adjacent
ToolbarItems are merged into the same glass background group by default. ToolbarSpacer(.fixed)forces a background group break, keeping favorite/collections as a separate group, with share and inspector each independent.- Use
ToolbarSpacer(.flexible, placement: .bottomBar)for leading/trailing stretch (like Mail’s filter and compose at opposite ends of bottomBar). - To opt a single item out of the background group (like an avatar), use
.sharedBackgroundVisibility(.hidden); to add a red dot, use.badge(count).
Search: put searchable on NavigationSplitView, the system places it at the bottom on iPhone and top trailing on iPad/Mac (11:44).
// Search in the top-trailing position
struct TopTrailingSearch: View {
@State private var searchText = ""
var body: some View {
NavigationSplitView {
SidebarContent()
} detail: {
DetailContent()
}
.searchable(text: $searchText)
}
}
Key points:
searchableonNavigationSplitViewmeans search applies to the entire view, not a single column.- iPhone automatically moves the search bar to the bottom for thumb reach; iPad/Mac automatically places it at toolbar trailing.
- Multi-tab apps wanting a dedicated search page:
Tab(role: .search) { ... }+searchableonTabView; when users switch to that tab, the search bar replaces the tab bar. - To force minimization to a toolbar button, use
.searchToolbarBehavior(.minimize).
Controls: buttons default to capsule shape (small size on macOS remains rounded rectangle); new .glass / .glassProminent styles (15:33).
// Prominent glass button
Button("Get Started") { }
.buttonStyle(.glassProminent)
// Standard glass button
Button("Learn More") { }
.buttonStyle(.glass)
Key points:
.glassProminentis for calls-to-action (CTA), with automatic tint..glassis a standard glass button, background adapts to the content below.- Slider adds tick marks (auto-generated from
step) andneutralValue(specifies offset from zero, for two-way values like EQ). - Use
.rect(corner: .containerConcentric)for concentric corners, avoiding manual corner radius calculation.
Custom Liquid Glass: add .glassEffect() to any view, use GlassEffectContainer to combine multiple elements for morphing (19:51).
// GlassEffectContainer
@Namespace var namespace
GlassEffectContainer {
VStack {
if isExpanded {
VStack(spacing: 16) {
ForEach(badges) { badge in
BadgeLabel(badge: badge)
.glassEffect()
.glassEffectID(badge.id, in: namespace)
}
}
}
BadgeToggle()
.buttonStyle(.glass)
.glassEffectID("badgeToggle", in: namespace)
}
}
Key points:
GlassEffectContainerlets multiple glass elements share a sampling region, avoiding visual fragmentation from independent sampling.glassEffectIDwith the same@Namespacecreates fluid morph transitions between elements during expand/collapse.- Individual elements can use
.glassEffect(.regular.tint(.green))for color,.glassEffect(.regular.interactive())for press scaling and highlights.
Key Takeaways
-
What to do: recompile your project with Xcode 26 first, note which UI automatically improves and what breaks.
- Why it’s worth it: many new effects (toolbar grouping, sheet embedded glass, scroll edge effect) come with zero code—this pass can save half the migration work.
- How to start: switch to Xcode 26 on a new branch, build & run each major page, put visual comparison screenshots in a review doc, then decide which APIs need changes based on the issue list.
-
What to do: clean up hacks added in older versions to fight toolbar/sheet defaults (custom backgrounds, translucent overlays,
presentationBackground).- Why it’s worth it: the new design’s automatic scroll edge effect and Liquid Glass material will clash with these old hacks—the more you keep, the worse it looks.
- How to start: search for
presentationBackground,.background(.regularMaterial), customColor.black.opacityin your project, evaluate each for deletion to see the default effect.
-
What to do: regroup toolbar items by semantics, express explicitly with
ToolbarSpacer(.fixed).- Why it’s worth it: users in the new design judge which buttons belong together by glass background continuity (like “favorite + add to collection” as a group, share and inspector independent). Ambiguous toolbars cause mis-taps.
- How to start: list all toolbar buttons per page, group “related actions” in brackets; insert
ToolbarSpacer(.fixed)between each two groups; add.sharedBackgroundVisibility(.hidden)to items outside groups.
-
What to do: move
searchableup to theNavigationSplitViewlevel, delete dual layouts written for iPhone/iPad.- Why it’s worth it: the system automatically places the search bar at the bottom on iPhone and toolbar trailing on iPad/Mac—one codebase works for both.
- How to start: move existing
searchablefrom the detail column to the outerNavigationSplitView; if search isn’t core, add.searchToolbarBehavior(.minimize)for auto-collapse.
-
What to do: try
glassEffect+GlassEffectContaineron custom controls, see if you can replace hand-written card styles.- Why it’s worth it: custom controls wrapped in glass match system components visually, and
GlassEffectContainerprovides fluid morphing hard to achieve with hand-written animations. - How to start: pick a custom component with expand/collapse interaction (like floating toolbar, emoji panel) as experiment, add
glassEffectIDto each sub-element, wrap the outer layer inGlassEffectContainer, compare old and new animation effects.
- Why it’s worth it: custom controls wrapped in glass match system components visually, and
Related Sessions
- Meet Liquid Glass — Design principles and optical properties of the Liquid Glass material.
- Get to know the new design system — Best practices and usage rules for the new cross-platform design system.
- Build a UIKit app with the new design — UIKit-side migration guide on the same theme, read alongside this session.
- Better together: SwiftUI and RealityKit — New ways to mix SwiftUI and RealityKit in visionOS 26.
- Make a big impact with small writing changes — Amplifying effect of copy tweaks under the new design.
Comments
GitHub Issues · utterances