How it works
PreviewModifier is a protocol you adopt to wrap a preview in shared setup before SwiftUI renders it, and to build any expensive or asynchronous context that setup depends on exactly once. It solves a common preview problem: many previews need the same scaffolding — sample data, an injected environment, a configured model store — and recomputing that per preview is wasteful and repetitive. Reach for PreviewModifier when several previews should share identical context, or when establishing that context requires async work that you'd rather perform a single time. SwiftUI caches the shared context by type and reuses it across every preview that applies the same modifier.
Conform a type to
PreviewModifierDeclare a value type that adopts the protocol so it can participate in preview rendering. Here
SampleDatais a smallstructconforming toPreviewModifier, giving you one place to define both the data and how it's applied to each preview that uses it.Produce the shared context in
makeSharedContext()Implement the asynchronous
makeSharedContext()requirement to build the value the previews share; SwiftUI runs it once and reuses the result. Its return type defines the modifier'sContextassociated type —makeSharedContext()returns[String](["Ada", "Linus", "Grace"]), and because it'sasync throwsyou canawaitreal setup work or surface failures.Apply the context in
body(content:context:)Implement
body(content:context:)to transform the preview'scontentusing the shared value, returningsome View. Thecontentparameter is the preview SwiftUI hands you, and thecontextparameter is whatevermakeSharedContext()produced; herebodyinjects it withcontent.environment(\.previewNames, context)so downstream views can read it.Read the injected context downstream
Because the modifier feeds context through the environment, the previewed view consumes it with the matching property wrapper. The demo exposes a
previewNamesEnvironmentValueskey (backed byPreviewNamesKey) and reads it via@Environment(\.previewNames) private var names, the same channelbodywrote to.
makeSharedContext() (e.g. change ["Ada", "Linus", "Grace"] to ["Ada", "Linus", "Grace", "Alan"]) and observe the shared context flow into the preview's environment.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct PreviewModifierDemo: View {
struct SampleData: PreviewModifier {
func makeSharedContext() async throws -> [String] {
["Ada", "Linus", "Grace"]
}
func body(content: Content, context: [String]) -> some View {
content.environment(\.previewNames, context)
}
}
@Environment(\.previewNames) private var names
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text("Shared Preview Context")
.font(.headline)
ForEach(["Ada", "Linus", "Grace"], id: \.self) { name in
Label(name, systemImage: "person.circle")
}
}
.padding()
}
}
private struct PreviewNamesKey: EnvironmentKey {
static let defaultValue: [String] = []
}
extension EnvironmentValues {
var previewNames: [String] {
get { self[PreviewNamesKey.self] }
set { self[PreviewNamesKey.self] = newValue }
}
}