Starting with Xcode 15 and Swift 5.9, you can create previews for your SwiftUI view using the new #preview Macro. The macro replaces the PreviewProvider
protocol that required us to define previews within a static computed property.
Swift 5.9 also introduced Macros, the concept behind this new hashtag attribute. I encourage you to read Swift Macros: Extend Swift with New Kinds of Expressions to understand the concepts before diving deeper into this new way of previewing SwiftUI views.
Creating a SwiftUI #Preview
Creating a preview for one of your SwiftUI views can be done as follows:
struct ContentView: View {
var body: some View {
VStack {
Text("Hello World!")
}
}
}
#Preview {
ContentView()
}
Previously, we would’ve written the same preview configuration as follows:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The Macro simplifies the definition of previews and makes it easy to provide titles for your previews:
#Preview("Hello World!") {
ContentView()
}
Defining multiple previews is as easy as defining multiple macro definitions. For example, to create multiple configurations of an article view:
Previewing UIKit and AppKit
Unlike the PreviewProvider
protocol, the #Preview macro lets you create previews for UIKit and AppKit views. This is incredibly valuable for older projects that couldn’t benefit from SwiftUI back in the day.
Imagine having a similar article view as before, but defined in UIKit:
import UIKit
final class ArticleViewController: UIViewController {
var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .title1)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
We can now create a preview using the macro as follows:
#Preview {
let articleViewController = ArticleViewController(nibName: nil, bundle: nil)
articleViewController.titleLabel.text = "Previews for UIKit work!"
return articleViewController
}
And Xcode will show you a preview accordingly:
Can I use #Preview on iOS 16 and below?
While the new way of creating previews works great on iOS 17 and up, you’ll notice it doesn’t work when your project targets lower versions:
‘Preview’ is only available in iOS 17.0 or newer
We can confirm this behavior by right-clicking the #Preview macro and selecting “Expand Macro.” Xcode will show the code generated by the Macro:
These compiler errors show up even if you select the iOS 17 Simulator. You could argue that you’ll only preview views using iOS 17, but your project won’t even build for running on iOS 16 and below.
While I’ve looked into compiler directives to work around this issue, it seems impossible in the current Xcode 15 beta versions to use previews within projects targeting iOS 16 or below. During WWDC 23, the development team replied to this issue in Slack:
Yes, both of those cases with backwards deployment are known issues with the #Preview macro at this time.
In other words, let’s hope for a future fix that allows us to use the #Preview macro in projects targeting older OS versions.
Conclusion
The new #Preview macro simplifies how we define previews for our SwiftUI views. The macro also allows defining previews for UIKit and AppKit views, which can significantly benefit older app projects.
If you like to improve your Xcode knowledge, even more, check out the Xcode category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.
Thanks!