Recommended Reading

18 Minutes
Boost Your SwiftUI Workflow with Live Previews in Xcode
Fix Bugs Faster! Log Collection Made Easy
Introduction
Introduced by Apple in 2019, SwiftUI is a user interface toolkit that provides some radical improvements to how developers built apps for iOS, macOS, watchOS and tvOS. One of its most impressive features is SwiftUI Preview, which lets developers visualize real-time Xcode updates to their UI design. SwiftUI Preview gives instant visual feedback, enhancing the development process as it helps developers quickly iterate and refine their interface.
In this post, we will take a deep dive into what SwiftUI Previews are and explore their key benefits and features. We’ll then look at how to use them, followed by some advanced tricks and best practices to help you get the most out of SwiftUI Previews.
This guide is drafted to help your development work, discussing the best practices for building quality user interfaces in a fast and efficient manner.
Table of Contents
Prerequisites
Required tools and libraries: Before getting into SwiftUI Previews, you need the following library and tools:
- The latest Xcode, which has SwiftUI and the new Preview. It can be downloaded from the Mac App Store or directly from the Apple Developer website.
- SwiftUI is a framework, provided by Apple and included with Xcode, that can be used to build Declarative User Interfaces.
- Start by ensuring that your macOS version is the latest to have all dependencies ready for running Xcode with SwiftUI.
Basic Knowledge
It’s best that you already understand the following to get the most out of this guide:
Proficiency in Swift: If you are new to the Swift programming language, learn some of its basics, including syntax and common structures, as this will help you understand many code examples and concepts.
Introduction to SwiftUI: Having a basic understanding of the fundamental concepts, like views, modifiers and layout structures provided by Apple, will help you understand the examples and techniques shown in this blog post.
With this groundwork in place, you’re now ready to take advantage of the powerful features SwiftUI Preview has to offer, and improve your UI development workflow.
Advantages of Using Previews
Real-Time Feedback: Being able to see UI changes happening in real-time is one of biggest advantages of using SwiftUI Previews. This causes the Live Preview to update as soon as changes are made to your SwiftUI code. In turn, this feedback loop helps with rapidly detecting and fixing problems, adjusting the design of a component, and trying out different layouts or looks without building and running your app.
Rapid Iteration: Previews in SwiftUI make it easy to iterate on designs. You can also easily see the result of your changes in real time, without having to compile and run the project after every change. This accelerates the development cycle, helping you try different ideas quickly and iterate on your design more effectively.
Visual Debugging: UI is hard to debug, but SwiftUI Previews helps a lot. You can tell if something is off – misalignment of layout problems, for example – by simply looking at your UI. You can also try an app out in the interactive preview, thus validating that your UI responds to user interaction as expected.
Multiple Device Previews: SwiftUI Previews lets you view your UI from the perspective of many different devices and screen sizes at the same time. This means your design can be made to fit well across a wide range of iOS devices, including iPhones and iPads with various screen sizes, and even in different orientations. It is important to have this capability for maintaining consistent and user-friendly experiences on all the supported devices.
Dynamic Type & Accessibility: The Dynamic Type & Accessibility adjustments of SwiftUI Previews shows how your app UI looks when different font sizes are selected by the user. This helps make your app available for all users, regardless of their font size preference, and by previewing these settings you can build interfaces that are more inclusive and user-friendly.
SwiftUI Previews bring a lot of benefits to the development process, including real-time feedback and rapid iteration, visual debugging and multiple device previews. These powerful features help deliver faster applications that are responsive by design, and with less effort.
Step-by-Step Guide to Setup SwiftUI Previews
This is a pretty straightforward procedure, and can be completed in a few steps while creating a new Basic SwiftUI Preview. This section will introduce you to the concept of building your first SwiftUI Preview, and help you understand the benefits of doing so.
- Create a New SwiftUI Project
- In xCode, click on File > New > File
- Choose template: Select the
App
template under theiOS
section, then click Next. - Give your project a name: Type the name of your project (example
TestingSwiftUIApp
), pick and choose what you want, then clickNext
. - Save your project: Select a place to save and click on
Create
2: Create a SwiftUI View
- Creating a new SwiftUI view:
File > New > File
, from the menu. - Select SwiftUI view: When Xcode allows you to choose a template, select
SwiftUI
View
and clicknext
. - Name the view: Give a name to your view
ContentView
and clickCreate
.
3: Write Your SwiftUI Code
With the ContentView.swift
created, open the above Swift file, then enter your SwiftUI code.
//Create a very basic view with text label.
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello!")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
4: Open the Preview Canvas: Click on the Resume
button in the upper-right corner of the Xcode window or hit CMD + Option + P
. A new window will be opened next to your source code editor showing the UI, as described in ContentView
structure. The text “Hello, SwiftUI!” is visible with padding.
5: Changes and Edits in Preview
Update Your Code: Replace the previous ContentView.swift
in the SwiftUI file.
//Create background with updated text and background color
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Welcome to Previews!")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Live Updates: The app preview canvas updates automatically to reflect your changes in real-time when you are writing your code.
Customizing SwiftUI Previews
Preview Modifiers: SwiftUI Preview provides different modifiers to help change the look and behavior of your previews. These modifiers represent how your UI will look and behave on different devices and screen sizes, and in any environment.
Preview Layouts: The .previewLayout()
modifier is designed to let you change how your view appears inside the canvas space. SwiftUI Previews use a layout for the device which is in place by default, but you can change this to whatever makes sense for you.
//How to use .previewLayout()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.fixed(width: 300, height: 100))
}
}
Device Previews: The .previewDevice()
modifier is a way of simulating different devices. That way, you can make sure your design fits well with different screen sizes and orientations.
//Example use .previewDevice()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewDevice("iPhone14Pro")
.previewDisplayName("iPhone14Pro")
ContentView()
.previewDevice("iPadPro(12.9-inch)")
.previewDisplayName("iPadPro(12.9-inch)")
}
}
}
Preview Sizes: The .previewLayout(.sizeThatFits)
modifier is used to adjust with inbound content. It’s particularly helpful for views that are designed to resize themselves based on their content.
//Example use .previewLayout()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.sizeThatFits)
.padding()
.background(Color.gray)
}
}
Combining Modifiers: Compose PreviewModifiers by using a different combination of preview modifiers to further refine your previews. With this, you get a nice overall picture of how your UI handles different scenarios.
//Example showing the combining of preview modifiers.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewLayout(.sizeThatFits)
.padding()
.background(Color.gray)
.previewDisplayName("Size_That_Fits")
ContentView()
.previewDevice("iPhone14Pro")
.previewDisplayName("iPhone14Pro")
.previewLayout(.device)
ContentView()
.previewDevice("iPadPro(12.9-inch)")
.previewDisplayName("iPadPro(12.9-inch)")
.previewLayout(.device)
}
}
}
Advanced Preview Techniques
Previewing Multiple States: You should use SwiftUI Preview to create a bunch of previews for various view states. This can be useful to see how your UI deals with different data inputs and conditions.
//multiple states example handling the different situations
struct ContentView: View {
var text: String
var body: some View {
Text(text)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView(text: "DefaultState")
.previewDisplayName("DefaultState")
ContentView(text: "ErrorState")
.previewDisplayName("ErrorState")
.background(Color.red)
ContentView(text: "SuccessState")
.previewDisplayName("SuccessState")
.background(Color.green)
}
}
}
Dynamic Type and Dark Mode: One great aspect of SwiftUI Previews is that they show the dynamic type with dark mode automatically. This means you can see how your UI reacts to different text sizes and color schemes.
//Example showing dynamic type and DarkMode
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView(text: "DynamicType")
.previewLayout(.sizeThatFits)
.environment(\\.sizeCategory, .extraSmall)
.previewDisplayName("ExtraSmallText")
ContentView(text: "DynamicType")
.previewLayout(.sizeThatFits)
.environment(\\.sizeCategory, .extraExtraExtraLarge)
.previewDisplayName("ExtraExtraExtraLargeText")
ContentView(text: "DarkMode")
.previewLayout(.sizeThatFits)
.environment(\\.colorScheme, .dark)
.previewDisplayName("DarkMode")
ContentView(text: "LightMode")
.previewLayout(.sizeThatFits)
.environment(\\.colorScheme, .light)
.previewDisplayName("LightMode")
}
}
}
Using Preview Providers: If you have a more complex scenario, consider writing custom preview providers to keep your previews organized and easily managed. This is especially useful when you need to preview the views with different configurations or dependencies.
//Example handling the complex scenarios
struct CustomPreviewProvider: PreviewProvider {
static var previews: some View {
Group {
CustomView(configuration: .init(state: .loading))
.previewDisplayName("LoadingState")
CustomView(configuration: .init(state: .loaded, data: "SampleData"))
.previewDisplayName("LoadedState")
CustomView(configuration: .init(state: .error, errorMessage: "SomethingWentWrong"))
.previewDisplayName("ErrorState")
}
}
}
struct CustomView: View {
var configuration: Configuration
var body: some View {
VStack {
switch configuration.state {
case .loading:
Text("Loading...")
case .loaded:
Text(configuration.data ?? "No Data")
case .error:
Text(configuration.errorMessage ?? "Error")
.foregroundColor(.red)
}
}
.padding()
}
}
struct Configuration {
enum State {
case loading, loaded, error
}
var state: State
var data: String? = nil
var errorMessage: String? = nil
}
struct CustomView_Previews: PreviewProvider {
static var previews: some View {
CustomPreviewProvider.previews
}
}
Best Practices for Using SwiftUI Previews
1. Modularize Your Code: Create your UI in multiple small, reusable components. This makes your code easier to manage, and means you can preview each component separately.
//Example showing the making reusable component
struct RecipeTitleView: View {
var title: String
var body: some View {
Text(title)
.font(.headline)
.padding()
}
}
struct RecipeTitleView_Previews: PreviewProvider {
static var previews: some View {
RecipeTitleView(title: "SampleRecipe")
.previewLayout(.sizeThatFits)
}
}
2. Use Group to Organize the Previews: Here we have many previews created, which are grouped under the Group
. This way, your previews are more structured and manageable.
When creating multiple previews, use Group
to organize them. This makes your previews easier to manage and understand.
// Usage of Group
struct RecipeDetailView_Previews: PreviewProvider {
static var previews: some View {
Group {
// here you can manage multiple previews
}
}
}
3. Test Different Configurations: Create previews for all different trait configurations, including those with dynamic type sizes, varying color schemes and different devices and displays, to makes your UI reactive and user-friendly.
RecipeDetailView(recipe: sampleRecipes[0])
.previewDevice("iPhoneSE(2nd generation)")
.previewDisplayName("iPhoneSE")
4. Leverage Custom Preview Providers: In more complicated scenarios, custom preview providers can help manage various states and configurations.
struct CustomPreviewProvider: PreviewProvider {
static var previews: some View {
Group {
// Custom previews
}
}
}
5. Utilize Preview Modifiers: Use preview modifiers like .previewLayout()
, .previewDevice()
. You can also use .environment()
to make your previews more user-friendly and test different conditions.
.previewLayout(.sizeThatFits)
.previewDevice("iPhone14Pro")
.environment(\\.colorScheme, .dark)
Performance Tips
1. Optimize View Hierarchy: Decompose complex view hierarchies to smaller, duplicatable building blocks. Do not use deep-nested views if you want to avoid performance impact.
2. Use Conditional Compilation: Safely add the code you want to run only in debug mode with the #if DEBUG
, thus avoiding cluttering your production code.
#if DEBUG
// Debug-only code, will not execute in release build
#endif
3. Limit the Number of Previews: Limit the number of simultaneous preview states to important views and states Group
modifier to keep your previews organized.
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
// Other previews
}
}
}
4. Use Lightweight Data: Use mock data or lightweight versions instead of fetching the original data from network, as that will make making heavy computations.
let sample = ["SampleItem1", "SampleItem2"]
5. Optimize Rendering: Use the .sizeThatFits
layout to ensure the view adapts to its content size and avoids over-rendering. Apply the .padding()
and use .background()
modifier to see it in the context of fixed size.
ContentView()
.previewLayout(.sizeThatFits)
.padding()
.background(Color.gray)
Conclusion
SwiftUI Previews enhance the app development process and enable real-time feedback, fast iteration and visual debugging. This blog post covered what previews are, and how they help developers get realtime feedback to fix UI issues without running the application.
We covered how to add the simplest kind of previews, and modify them with many common modifiers, such as Previews in Multiple States, Dynamic Type and Dark Mode, as well as defining our own Custom Preview Providers. We also included examples of how the feature would behave in real life, along with best practices for good usage, and what to watch out for.
Now that you understand SwiftUI Previews at a high level, we recommend playing around with this concept and exploring more of it on your own. Make experience rendering and try states for views, for example, and play with size classes of your UIs on various devices and with different screen sizes and orientations.
You should also try adjusting your previews with preview modifiers to make your designs more responsive and accessible. Lastly, we discussed the best practices along with performance improvement tips in your application. Happy coding.
Expect The Unexpected!
Debug Faster With Bugfender