WWDC Notes: What’s new in SwiftUI

Published on: June 9, 2021

A good way to get started with SwiftUI is to use it for new features. SwiftUI can be mixed in with UIKit and AppKit code. It also allows you to expand into new platforms, like macOS, with little to no work.

Essentially, try to do new work with SwiftUI whenever you can.

Better lists

SwiftUI can load images async with the new AsyncImage. This takes a URL and shows a placeholder by default. You can pass a closure to configure the loaded image with modifier, and to set a custom placeholder.

There’s a new refreshable modifier. This modifier takes a closure that takes an async closure. So you can await things in there. Lists use this modifier to add pull to refresh.

You can run a task on appear with the task modifier. This will run when the view is first created, and cancels the task when the view disappears. You could use this to fetch initial data for the list without blocking your UI.


  • Discover concurrency in SwiftUI
  • Swift concurrency: Update a sample app

Text can be made editable with a TextField. This takes a binding but we might not easily have a binding available because in a list we receive an element; not a bindable object.

This is covered in depth in Demystifying SwiftUI.

You can pass a binding to your collection to the List. This will make it so the item closure receives a binding too. You can still read data like you did normally but you also have a binding.

Looks like you should use $thing instead of thing in the list item closure.

Separator tints can be set with listRowSeparatorTint. Also works for section separators. listRowSeparator(.hidden) hides the separators.

SwiftUI now has a swipeActions modifier that allows you to add swipe actions to items in ForEach. You can pass an edge to swipeAction to control where they appear. Also works on macOS.

View styles now have an enum-like style. For example listStyle(.inset). We can now pass a value to inset to alternate row background colors.

Beyond lists

We can now create tables with the Table view. Tables use a TableColumn for columns. They take a title + data. They support single + multiple selection and sorting. You can sort through key paths. Worth exploring, looks fun.

Fetch requests now provide a binding to their sort descriptor that allows you to sort fetched results Must explore

We can also use @SectionedFetchRequest to split @FetchRequest into sections.


  • SwiftUI on the Mac: Fundamentals
  • SwiftUI on the Mac: Finishing touches
  • Bringing Core Data concurrency to Swift and SwiftUI

Lists in SwiftUI can now be made searchable through a modifier. It binds to a search query. There’s a whole session on each in SwiftUI.

We can use onDrag to make this draggable (existing) we can provide a preview: closure to provide a custom preview of the dragged data.

We can add the importsItemProviders to make a view a drop target that accepts item providers. We can add ImportFromDevicesCommands() to an app’s commands to allow import from external devices like a camera.

We can use exportsItemProviders modifier to export items so that apps like Shortcuts can use our data. Not sure how this works exactly though. Covered only very briefly.

Advanced graphics

Apple added lots of new SF Symbols and two new rendering modes. Hierarchical and palette. More information in the SF Symbols talk.

There are all kinds of new colors in SwiftUI this year.

SF Symbol variants are now automatically picked by the system. Modifiers like .fill or no longer needed. The correct variant is automatically chosen based on context and platform. This is covered more in the SwiftUI + SF Symbols talk.

A Canvas can be used to draw a large number of items that don’t need to be managed individually. It’s possible to add gestures to a Canvas and modify its contents. You can add accessibility information like accessibillityChildren to enhance the accessibility experience.

You can wrap a TimelineView to create a timeline loop to build a screensaver like experience for tvOS. Or you can even use it to update your watch app when it’s in the always on state. You could, for example, update your view every minute. More information in What’s new in WatchOS 8.

.privacySensitive is a useful modifier to hide views when they might be visible to others. For example when a device is locked.

Materials can be used to build blurry translucent backgrounds and more. Materials work with the safe area inset to build partial overlays for example. The Rich graphics session covers this in depth.

SwiftUI previews can now show landscape iOS devices. Accessibility modifiers are now shown in the inspector when using previews. You can also inspect the state of your accessibility with previews. Covered in depth in the accessibility session.

Text and keyboard (focussed based)

SwiftUI Text now supports markdown to format text and even include links or code. This is built on top of Swift Attributed strings. More information on this in What’s new in Foundation.

Xcode 13 can now extract localization catalogs at compile time. Check out Localize your SwiftUI app for more.

Dynamic type was already supported. Now we can set a dynamicTypeSize minimum and maximum size to ensure our layouts don’t break for too large or small texts.

We can enable text selection on macOS with .textSelection. All text within the view this is applied to will be selectable. Also works on iOS.

We can easily format dates with .formatted. We can even use this to format people names for example. We can bind text fields to formatted fields. PersonNameComponents is used in an example. Also covered in the Foundation talk.

We can use onSubmit to handle pressing of the return key. We can also apply this on an entire form. We can set the label of the return key through the submitLabel modifier.

We can add toolbars to a keyboard through the toolbar modifier by returning a ToolbarItemGroup with placement: .keyboard. Shown above keyboard on iOS/iPadOS and Touch Bar on Mac.

We use @FocusState to control focus state. The focused modifier takes a binding to a focus state and will change this state when something is focused. This allows you to update something else in your view.

@FocusState can represent a Hashable value and .focused($focusField, equals: .addAttendee) to for example bind to an enum value.

When you set the @FocusState property to a value, the view that has its focused(_:equals:) set to that value will be activated.

Keyboard can be dismissed by not having a selected focus state. More information in the SwiftUI + Focus session.


Buttons are used all over the place in SwiftUI. You can use the .bordered button style to add a button around buttons. You can add tinting and apply the modifier to a container to style all buttons.

You can use controlSize and controlProminence to set a button’s styling too. A button that has an increased prominence gets high contrast color to stand out. accentColor is more dimmed. Button size .large makes a large full width button.

You can use a maxWidth to prevent a button from being too big. You can apply a keyboardShortcut to activate a button.

High prominence should not be applied to all buttons. It’s best for 1 button per screen. A lower prominence can be used to add a splash of color. You can mark buttons as destructive now so they are colored red. You can also ask confirmation for (destructive only?) buttons with the confirmationDialog.

Menu on macOS can now have a primary action and a menu with options of secondary actions. This also works on iOS.

New on button is a toggle style to activate / deactivate buttons.

We can compose everything together in a control group for example.


WWDC21 Notes

Subscribe to my newsletter