PreviewProvider is a SwiftUI protocol that defines the entry point for generating previews in Xcode Canvas. Implementing the protocol allows the developer to see the interface without launching the simulator, speeding up iteration during layout stage. According to Apple Developer Documentation (2026), PreviewProvider is mandatory for all SwiftUI Views if the project uses Canvas — without it, Canvas does not display the user interface. Learn more in the article about SwiftUI.
Key Takeaways
PreviewProvider is a SwiftUI protocol that defines a contract for creating preview content in Xcode Canvas. The protocol contains a single required property: previews of type some View. Any value returned by previews is displayed in Canvas as an interactive preview. PreviewProvider does not require inheritance — a static implementation in an extension is sufficient.
Architecturally, PreviewProvider is not part of the SwiftUI runtime — it is purely a development tool. The protocol is marked with the @available(iOS 13.0, *) attribute and is not compiled into release builds, as Xcode uses conditional compilation to exclude preview code from production. This means PreviewProvider does not affect binary size or application performance.
The previews property is the sole requirement of PreviewProvider. It must return any View: from a simple Text to a complex hierarchy with Group and ForEach. Xcode renders the returned View in Canvas, applying system settings (theme, size, font).
import SwiftUI
struct GreetingView: View {
let name: String
var body: some View {
Text("Hello, \(name)!")
.padding()
}
}
// PreviewProvider — static implementation
struct GreetingView_Previews: PreviewProvider {
static var previews: some View {
GreetingView(name: "World")
}
}
Naming convention: Apple recommends naming the preview struct as {ViewName}_Previews. This is not a compiler requirement, but it improves readability and project navigation. Xcode automatically inserts this template when creating a new SwiftUI file.
The working mechanism of PreviewProvider is based on static dispatch: Xcode compiles the PreviewProvider extension only for Debug configuration and calls previews during the Canvas building process. Each time the code changes, Xcode recompiles only the modified PreviewProviders, ensuring near-instant preview updates.
SwiftUI does not guarantee an exact match between the preview and the final UI on a simulator or device — Canvas uses simplified rendering. Animations with delays may display incorrectly, and some UIKit components (MapKit, WebView) do not render in Canvas without additional configuration.
Group allows displaying several states of one View simultaneously, speeding up iteration when laying out different configurations. Each preview inside Group renders independently.
struct ButtonView_Previews: PreviewProvider {
static var previews: some View {
Group {
ButtonView(title: "Primary", style: .primary)
.previewDisplayName("Primary")
ButtonView(title: "Disabled", style: .primary)
.disabled(true)
.previewDisplayName("Disabled")
ButtonView(title: "Secondary", style: .secondary)
.previewDisplayName("Secondary")
}
}
}
previewDisplayName adds a label to each preview in Canvas, which is especially useful when comparing multiple states. The maximum number of previews in Group is not limited, but more than 6–8 slow down Canvas.
Xcode provides several modifiers for configuring preview display. The main ones: previewDevice — emulates a specific device (iPhone 16 Pro, iPad Air, Apple Watch Ultra), previewLayout — sets the size (device, fixed, sizeThatFits). The combination of these modifiers gives full control over the preview environment.
previewDevice accepts a string with the device name, for example "iPhone 16 Pro" or "iPad Pro 13-inch (M4)". The list of available devices depends on the simulators installed in Xcode. If the device is not found, Canvas displays the preview on the default device without an error.
| Modifier | Description | Example |
|---|---|---|
| previewDevice | Device emulation | .previewDevice("iPhone 16 Pro") |
| previewLayout | Size mode | .previewLayout(.sizeThatFits) |
| previewDisplayName | Preview label | .previewDisplayName("Dark Mode") |
| preferredColorScheme | Color scheme | .preferredColorScheme(.dark) |
| dynamicTypeSize | Font size | .dynamicTypeSize(.xxxLarge) |
Common practice is to show one View on multiple devices simultaneously to check responsiveness. For this, ForEach with an array of device names is used.
struct AdaptiveView_Previews: PreviewProvider {
static var previews: some View {
ForEach(["iPhone SE (3rd generation)", "iPhone 16 Pro Max", "iPad Pro 13-inch (M4)"], id: \.self) { device in
AdaptiveView()
.previewDevice(.previewDevice(device))
.previewDisplayName(device)
}
}
}
Practical examples demonstrate various PreviewProvider usage scenarios: from simple previews to complex configurations with live data and UIKit compatibility.
Mock data is a standard pattern for previews when a View accepts a model. Instead of a real API, test data is substituted, allowing visual verification of the UI state without launching the application.
struct UserProfileView: View {
let user: User
var body: some View {
VStack {
AsyncImage(url: user.avatarURL)
.clipShape(Circle())
Text(user.name)
.font(.title)
Text(user.bio)
.font(.body)
.foregroundColor(.secondary)
}
}
}
struct UserProfileView_Previews: PreviewProvider {
static var previews: some View {
UserProfileView(user: .mock)
.previewDisplayName("Profile")
UserProfileView(user: .mockLongName)
.previewDisplayName("Long Name")
}
}
UIKit compatibility — PreviewProvider also works with UIKit components wrapped in UIViewRepresentable. This allows previewing existing UIKit views in SwiftUI Canvas without migrating the entire project.
struct MapViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
MKMapView()
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// Configure map
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapViewRepresentable()
}
}
Canvas is Xcode's visual editor that renders the output of PreviewProvider in real time. Without a PreviewProvider implementation, Canvas remains empty. Canvas and PreviewProvider work as a pair: PreviewProvider defines what to show, Canvas defines where and how.
Importantly, Canvas is the preview execution environment, not an alternative to PreviewProvider. Even if the developer does not open Canvas, PreviewProvider can be used for quick code checking via the popup preview when hovering over the Canvas icon. According to WWDC 2024, Apple recommends writing PreviewProvider for every View as a development standard, similar to writing unit tests.
| Component | Role | Requirement |
|---|---|---|
| PreviewProvider | Defines preview content | Required for Canvas |
| Canvas | Renders preview in editor | Optional (can use .preview) |
| SwiftUI View | UI component | Required |
Recommendation: write PreviewProvider for every public View in the project. This accelerates onboarding of new developers, simplifies code review, and allows quick visual change verification without building the entire project.
Issue 1: Preview does not update. If Canvas does not reflect code changes, the cause is usually the DerivedData cache. Clear DerivedData via Product → Clean Build Folder (⇧⌘K) or manually by deleting the ~/Library/Developer/Xcode/DerivedData folder. After cleaning, Canvas rebuilds the preview from scratch.
Issue 2: PreviewProvider does not see @StateObject. PreviewProvider creates a static instance of the View, so dependencies that require injection (ViewModels, services) must be passed through the initializer or @StateObject with a default value. Use mock objects instead of real services in previews.
Issue 3: Animations do not work in Canvas. Canvas does not support all SwiftUI animations — especially those that depend on timing (withAnimation with delay, .spring). For animation testing, run the application on a simulator. Canvas is suitable for static layout verification.
Dependency injection is the best way to make PreviewProvider work with complex ViewModels. Create a separate ViewModel instance with test data and pass it to the View initializer.
struct DashboardView: View {
@StateObject var viewModel: DashboardViewModel
var body: some View {
List(viewModel.items) { item in
Text(item.title)
}
}
}
struct DashboardView_Previews: PreviewProvider {
static var previews: some View {
DashboardView(viewModel: DashboardViewModel.mock)
}
}
Mock extensions: create an extension for the ViewModel that provides static .mock instances. This keeps test data close to the ViewModel and makes PreviewProvider readable.
Frequently Asked Questions
Technically no — the application will compile without PreviewProvider. However, in practice, Apple and the SwiftUI community recommend writing previews for every public View. PreviewProvider accelerates development, allows quick layout verification across different devices, and serves as visual documentation for the team.
PreviewProvider adds code only in Debug builds, so compilation errors can occur if the preview uses types unavailable in the release configuration. Errors also occur when using @available with platforms that do not support Canvas, or when exceeding the preview complexity limit.
Directly — not possible, PreviewProvider runs in isolation. Use mock data: create a static model extension with .mock instances. For Views with @StateObject, pass a ViewModel with test data through the initializer. This simulates real data without network requests.
No, PreviewProvider does not affect the release binary size. Xcode uses conditional compilation (#if DEBUG / #if !RELEASE) to exclude preview code from release builds. PreviewProvider code exists only in Debug configuration and does not end up in App Store builds.
Yes, Xcode supports preview debugging. Set a breakpoint inside previews or the View code itself and select Product → Preview → Debug Preview. The breakpoint will trigger during Canvas rendering. This is useful for analyzing layout issues that are only visible in previews.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also