The trait of a good look
It’s always important to have the right style in any look you go for. The right background. The right shadow. The right fit. The right feel. But by default, any use of a SwiftUI preview lacks all of these. It’s just the view, there without adornment for the world to see. So how can these be improved and built upon?
The answer to that is the use of traits and specifically PreviewTrait. So let’s unpack things a bit.
PreviewModifier
The first step is to create a modifier that will provide the look and style of the preview. It acts in the same way as ViewModifier that can be used on a View, but has the ability to also provide a Context. The context is what allows you to provide extra data such as setting up a database or creating appropriate mocks for the view.
Consider a spherical modifier in a vacuum
By default, the definition of PreviewModifier will define an associated type for Context as Void. This means that the modifier is just providing a look but avoiding the feel.
public struct BlackBackgroundModifier: PreviewModifier {
public func body(content: Content, context: Void) -> some View {
content
.background(Color.black)
}
}
As with ViewModifier it is typical to provide a convenience function for it so that it can be used. This would be defined like:
public extension PreviewTrait where T == Preview.ViewTraits {
static var blackBackground: Self = .modifier(BlackBackgroundModifier())
}
To make use of this new trait, we provide it when defining the Preview.
#Preview(traits: .blackBackground) {
YourAmazingView()
}
Giving it all a bit of love and feeling
A reference is all good and useful, but the view doesn’t feel alive unless it has the data it usually expects when it exists within your app. A typical approach here is when using SwiftData you can provide the Context as a ModelContainer. This is the example that is seen everywhere online, but to be honest it can be any type you want. The idea is that the Context gets created once and then it is reused throughout the Canvas lifecycle showing the Preview(s).
So lets work through this example. It sets up a model container and provides it to the view. You can get access to the Context from within the body function and then apply it to the view.
struct PreviewSampleData: PreviewModifier {
static func makeSharedContext() throws -> ModelContainer {
let config = ModelConfiguration(
isStoredInMemoryOnly: true
)
do {
let container = try ModelContainer(
for: FeelGoodJournalEntry.self,
configurations: config
)
try addSampleData(to: container)
return container
} catch {
fatalError("Couldn't create container: \(error.localizedDescription)")
}
}
func body(content: Content, context: ModelContainer) -> some View {
content.modelContainer(context)
}
}
The sky is the limit
So this is a simplified for teaching example of how a PreviewModifier and PreviewTrait can be used to provide look and feel to your previews. From here, it’s a matter of exploring and seeing how you can use them to improve the experience of your development within Xcode.