Skip to content
SwiftUI Previews: Tips to Boost Your Xcode Workflow

10 Minutes

SwiftUI Previews: Tips to Boost Your Xcode Workflow

Fix Bugs Faster! Log Collection Made Easy

Get started

SwiftUI Previews show us how our app will look out in the wild and let us make changes in real time, without emulators. But that’s not the full story.

The full benefit of SwiftUI Previews lies in declarativeUI, which allows us to dictate the final state we want to achieve and handles all the process stuff itself. This is a game-changer for developers, allowing us to shift our focus from ‘how’ to ‘what’.

Instead of rebuilding and relaunching your app every time you tweak a view, SwiftUI Previews let you see those changes instantly — right inside Xcode. Less time wasted on duplication, more time for the creative stuff that will set your app apart.

The release of XCode16 in September 2024 brought huge advances in SwiftUI—so it’s even more crucial that we know how to maximize the technology. In this post we’re going to take you right to the core, giving you all the tools you need to use SwiftUI

Introduction: What are SwiftUI previews, really?

The role of previews in Swift

Simply, a SwiftUI preview is an instance of your view that’s rendered in Xcode’s canvas. It runs in a lightweight simulator and lets you interact with UI elements, test different configurations and quickly validate design decisions.

Previews serve as your visual feedback loop, bridging the gap between design and implementation. Whether you’re adjusting padding, testing light and dark modes, or validating accessibility settings, SwiftUI previews help you iterate at lightning speed — without leaving your IDE.

SwiftUI / UIKit Previews are the future of interface design. Abdul Karim, seasoned iOS developer, writing on Medium in 2023.

The SwiftUI preview feature in Xcode 16

With Xcode 16, SwiftUI previews have become even more seamless and powerful.

Apple introduced a faster, more memory-efficient preview engine, meaning fewer rebuilds and smoother live updates. The new Dynamic Preview Refresh feature automatically updates your canvas when you edit code, assets, or even localization files.

Xcode 16 also enhances preview controls — you can now change orientation, device type, and appearance right from the preview inspector (fear not, we’ll come to this). Combined with the new #Preview macro introduced in Swift 5.9, previews are easier to declare, read, and maintain.

How can SwiftUI Previews benefit my app?

SwiftUI Previews drastically reduce iteration time by allowing immediate feedback on UI changes. Here are some specific benefits.

  • Rapid iteration: You can tweak your layout or animation and see the result instantly.
  • Better code organization: Each view gets its own preview, serving as a live demo and documentation.
  • Design validation: Test spacing, scaling and alignment scales across multiple devices and dynamic type sizes.
  • Easier testing: You can simulate mock data or error states without relying on a live backend.
  • Improved collaboration: Designers and PMs can visually inspect your work directly in Xcode.

For complex apps, previews function almost like UI unit tests — ensuring every view renders and behaves correctly under different conditions.

Exciting, right? Let’s go under the hood and start implementing.

Part 1: How to get started with SwiftUI previews

How to label previews

When you’re creating multiple preview configurations for a single view, it’s essential to keep them organized. Use .previewDisplayName() to label your previews for clarity.

struct ProfileCard_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ProfileCard(user: .mockStandard)
                .previewDisplayName("Standard User")

            ProfileCard(user: .mockPremium)
                .previewDisplayName("Premium User")
        }
    }
}

Labeled previews make it easier to distinguish between variations, especially in complex UI components.

Creating a super-simple preview

Let’s start with the “hello world” of SwiftUI previews. A minimal setup that displays your view instantly in the canvas.

With Swift 5.9 and Xcode 16, the #Preview macro has replaced the older PreviewProvider boilerplate.

struct WelcomeView: View {
    var body: some View {
        Text("Welcome to SwiftUI!")
            .font(.largeTitle)
            .padding()
    }
}

#Preview {
    WelcomeView()
}

Pro tip: Use the “Resume” button (or Option + Command + P) to refresh previews quickly after making code changes.

How to work with views that require parameters

Many views take parameters, like model data or configuration options. You can still preview them by providing mock values.

struct UserProfileView: View {
    var user: User

    var body: some View {
        VStack {
            Text(user.name)
                .font(.headline)
            Text(user.role)
                .foregroundColor(.secondary)
        }
        .padding()
    }
}

#Preview("Mock Data") {
    UserProfileView(user: .mock)
}

Using mock models or static data lets you simulate real-world states without depending on APIs or database connections.

How to work with views that require bindings

A @binding property establishes a two-way connection between your view and a piece of state data, enabling the view to both read and write to that particular data source.

When your view expects a @binding, SwiftUI previews allow you to use .constant().

struct SettingsView: View {
    @Binding var notificationsEnabled: Bool

    var body: some View {
        Toggle("Enable Notifications", isOn: $notificationsEnabled)
            .padding()
    }
}

#Preview("Enabled") {
    SettingsView(notificationsEnabled: .constant(true))
}

#Preview("Disabled") {
    SettingsView(notificationsEnabled: .constant(false))
}

This approach makes it easy to test both toggled states without needing real state management.

Using the UIViewController

This part is particularly important.

The UIViewController acts as a bridge between the SwiftUI view hierarchy and UIKit, Apple’s original framework for building user interfaces which has been around since the dawn of the iPhone. UIKit is essential to your technology, simulator and interface builder.

SwiftUI previews can render UIKit view controllers using UIViewControllerRepresentable , as this code shows:

struct LoginViewControllerPreview: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> some UIViewController {
        LoginViewController() // UIKit controller
    }

    func updateUIViewController(_ vc: UIViewController, context: Context) {}
}

#Preview("UIKit Login") {
    LoginViewControllerPreview()
}

This is particularly useful when you’re migrating from UIKit to SwiftUI or maintaining hybrid projects.

Part 2: More advanced use cases for SwiftUI Previews

Now let’s go through the gears. SwiftUI Previews empower us to try all kinds of layouts and orientations, enabling us to carry out A/B testing on our UX.

Quickly testing different layouts

If your layout adapts to different conditions — for instance, compact vs. regular size classes — you can easily preview both versions side by side.

#Preview("Compact Layout") {
    DashboardView()
        .previewLayout(.sizeThatFits)
}

#Preview("Full Layout") {
    DashboardView()
        .previewLayout(.device)
}

.sizeThatFits lets you see just the view’s intrinsic size, which is ideal for reusable components.

Experimenting with different view sizes

When working with dynamic layouts, it’s helpful to test specific dimensions. You can simulate various screen sizes or container widths to ensure responsive layouts work correctly.

Here’s some code to help you do that:

#Preview("Custom Size") {
    CardView()
        .previewLayout(.fixed(width: 300, height: 200))
}

Adjusting for light and dark modes

Up to 70% of Apple users have their devices in dark mode, so testing for dark environments is a non-negotiable.

SwiftUI Previews allow you to preview both light and dark appearances simultaneously and toggle appearance directly from Xcode’s preview inspector, which saves even more time.

#Preview("Light Mode") {
    ContentView()
        .preferredColorScheme(.light)
}

#Preview("Dark Mode") {
    ContentView()
        .preferredColorScheme(.dark)
}

Adjusting orientation

Orientation bugs often sneak in during development. Previews make it simple to test both portrait and landscape modes, which is especially useful for iPad apps and multi-scene layouts (we’ll delve deeper into multi-device views in the next section).

#Preview("Landscape") {
    ContentView()
        .previewInterfaceOrientation(.landscapeLeft)
}

3. How to use SwiftUI Previews across multiple devices

It goes without saying that your app is going to be used in several different contexts. iPhone Pro, iPad Air, Apple Watch… each with their own type size and color scheme.

SwifftUI Previews allow you to see compare different device contexts side by side, so you can achieve consistent layout and performance across the entire Apple universe (quick note: you can loop through multiple device names using ForEach).

This snippet renders your view on several devices at once — invaluable for ensuring design consistency across screen sizes.

#Preview("Device Previews") {
    ForEach(["iPhone SE (3rd generation)", "iPhone 15 Pro", "iPad Air (6th generation)"], id: \\.self) { device in
        ContentView()
            .previewDevice(PreviewDevice(rawValue: device))
            .previewDisplayName(device)
    }
}

How to preview multiple view instances

If your UI supports different data or visual states (e.g. loading, success, error), you can preview each state in isolation.

By visualizing multiple states simultaneously, you catch layout or state management issues early in development.

#Preview("Loading State") {
    DashboardView(state: .loading)
}

#Preview("Error State") {
    DashboardView(state: .error("Failed to load data"))
}

#Preview("Success State") {
    DashboardView(state: .success(mockData))
}

Some quick tips:

  • Use Group to preview multiple views at once.
  • Add names to your previews using the string argument.
  • Create a reusable preview wrapper if many of your views require a binding or model.
  • Combine all your variants into a single preview file.
  • Wrap preview-only helpers in #if DEBUG so they’re ignored in release builds:

Part 4: Common pitfalls and how to deal with them

Even though SwiftUI Previews are powerful, they’re not immune to glitches and it’s easy to make mistakes when you’re still getting used to them.

Here are common pitfalls and quick fixes for you to bear in mind.

ProblemFix / How-To
🐢 Slow or frozen previewsDisable Automatic Preview Updates for complex views. Refresh manually when needed (Option + click ▶ “Resume”).
⚠️ Missing assets or environment objectsProvide mock data or inject @EnvironmentObject instances manually in your preview to prevent crashes.
🌐 Network calls running in previewsAvoid real network requests. Replace with stubs, fake responses, or static mock data.
💥 Build errors or preview crashesClear Derived Data (Shift + Command + K) and restart Xcode. Cached previews often cause build issues.
🧱 Preview-only code appearing in release buildsWrap preview-specific helpers or test data in #if DEBUG … #endif so they’re excluded from production.

You may well run into more snags as you go. If and when you do, feel free to reach out and we’ll do our best to help.

Conclusion

SwiftUI previews aren’t just a nice-to-have — they’re a fundamental part of a modern, efficient iOS development workflow. They empower developers to iterate faster, validate designs instantly, and maintain high-quality code through visual testing.

With Xcode 16, previews are smoother, faster, and more capable than ever. Whether you’re refining a small component or testing an entire app screen, SwiftUI previews help you work smarter and deliver polished results.

By mastering SwiftUI previews, you’re not just saving compile time — you’re building confidence in your code and ensuring that every pixel in your UI behaves exactly as expected.

Questions after reading all that? If so, please get in touch. We’re always here to help. Otherwise, happy coding!

Expect The Unexpected!

Debug Faster With Bugfender

Start for Free
blog author

Aleix Ventayol

Aleix Ventayol is CEO and co-founder of Bugfender, with 20 years' experience building apps and solutions for clients like AVG, Qustodio, Primavera Sound and Levi's. As a former CTO and full-stack developer, Aleix is passionate about building tools that solve the real problems of app development and help teams build better software.

Join thousands of developers
and start fixing bugs faster than ever.