Adding previews to your interface files
Write code to test your views on different devices and configurations without needing to run your app.
Overview
Show previews of your views in the canvas by adding preview macros to your SwiftUI, UIKit, and AppKit files. The Swift preview macro is a snippet of code that displays and configures your view.
[Image]
You can change environment and device settings in the code or using controls in the canvas. To show the canvas next to the source editor, click Show Canvas in the upper-right corner of the toolbar. You can also give previews sample data to display that’s separate from your app.
For more information on using the canvas, see Interacting with previews in the canvas. To generate previews using coding intelligence instead, see Generate playgrounds and previews.
Add a preview macro
Add one of the #Preview macros to an interface file — such as Preview(_:body:) — to tell Xcode what to display in the canvas. In the body of the macro trailing closure, add code that creates and returns an instance of the view configuration you want to display.
For SwiftUI, you configure a view in a preview macro. For UIKit and AppKit, you can configure either a view or view controller in a preview macro.
Capture specific previews in code
In addition to the preview options Xcode provides, you can also customize and configure previews you want to reuse programmatically.
For example, you can add a name to more easily track what each preview displays. Xcode also uses the name that you pass to the macro as the label for that preview in the tab bar of the canvas.
// A preview with an assigned name.
#Preview("2x2 Grid Portrait") {
Content()
}Display a variant of the view by passing one or more configuration traits as a variadic argument list into the preview macro. For example, to display your view in the landscape left orientation, pass the landscapeLeft type property into the init(_:traits:body:) preview initializer to tell Xcode which orientation to display.
Tag dynamic properties for use in previews
When a view depends on a Binding property wrapper, you can create a functional binding for that property and pass it into your preview using the Previewable() macro. This macro works on any variable conforming to the DynamicProperty protocol.
struct PlayButton: View {
@Binding var isPlaying: Bool
var body: some View {
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
.resizable()
.scaledToFit()
.frame(maxWidth: 80)
}
}
}
#Preview {
// Tag the dynamic property with `Previewable`.
@Previewable @State var isPlaying = true
// Pass it into your view.
PlayButton(isPlaying: $isPlaying)
}Tagging a dynamic property with the Previewable macro eliminates the need to create wrapper views in previews.
Make complex objects reusable with a preview modifier
To avoid recreating expensive objects for every preview that needs them, in SwiftUI you can create these objects once with the PreviewModifier and then pass the preview modifier into your preview using the Preview(_:traits:_:body:) macro.
Expensive objects — such as objects that make network calls, perform disk access, or just take considerable time and effort to set up — can make your previews take longer to load. By creating these expensive objects once, and sharing them across all your previews, you make your previews more efficient.
For example, if you have an app with an expensive Observable() object:
@Observable
class AppState {
// An expensive, complex, bulky object.
var expensiveObject = "Some expensive object"
}
@main
struct MyApp: App {
@State private var appState = AppState()
var body: some Scene {
WindowGroup {
ComplexView()
.environment(appState)
}
}
}You reuse that expensive object across multiple views in your app:
struct ComplexView: View {
@Environment(AppState.self) var appState
var body: some View {
Text("\(appState.expensiveObject)")
}
}For every view you want to preview, you recreate and pass in that expensive object:
#Preview {
ComplexView()
// Potentially expensive if `AppState` is large or complex.
.environment(AppState())
}Instead, define the expensive object once and share it across multiple previews using the PreviewModifier protocol.
Define a structure conforming to the
PreviewModifierprotocol.Implement the static makeSharedContext() function returning the object with the expensive state.
Inject that shared context into the view you want to preview using the body(content:context:) function.
Add the modifier to the preview using the Preview(_:traits:_:body:) macro.
// Create a struct conforming to the PreviewModifier protocol.
struct SampleData: PreviewModifier {
// Define the object to share and return it as a shared context.
static func makeSharedContext() async throws -> AppState {
let appState = AppState()
appState.expensiveObject = "An expensive object to reuse in previews"
return appState
}
func body(content: Content, context: AppState) -> some View {
// Inject the object into the view to preview.
content
.environment(context)
}
}
// Add the modifier to the preview.
#Preview(traits: .modifier(SampleData())) {
ComplexView()
}Pass views only the data they need
When creating views, pass in only the data the view needs to display. Avoid passing in objects that fetch data; objects make setting up a view’s preview more complicated and less performant.
Instead, create views with the minimal amount of data they need, favoring simpler, immutable data types. Creating views this way makes testing and previewing your views easier and helps them perform better.
The following example shows how you can use simple data types, like String and enum, to preview a view in various ways using the preview macro.
Reduce your app size with development assets
To access resources in your previews, without shipping them in the final version of your app, use development assets in your Xcode project. Development assets give you access to resources such as images, video, JSON data, and code files in your previews, without increasing the size of your app.
Add items to the Development Assets of a target in Xcode as follows:
In the Project navigator, select the project.
In the project editor on the right, select the target.
In the General tab, scroll down to Development Assets.
In the lower-left corner, click the Add items button (+).
In the dialog that appears, select the items that you want to add and click Add.