Xcode 16 removed the boilerplate around previews. @Previewable inlines state into the
#Preview macro, and PreviewModifier builds a reusable, cached preview environment such as a
mock data container.
Inline state with @Previewable
A view with a @Binding used to need a wrapper view whose only job was to own the @State.
@Previewable declares that state inside the #Preview block instead. Using it outside
#Preview is a compiler error.
#Preview {
@Previewable @State var goal: TimeInterval = 8 * 3600
SleepDetailsView(snapshot: SleepSnapshot(), goal: $goal)
.preferredColorScheme(.dark)
}
@Previewable works with other SwiftUI property wrappers too, such as @Environment and
SwiftData's @Query.
Reusable environments with PreviewModifier
A PreviewModifier has two requirements. makeSharedContext builds whatever the preview needs,
for example an in-memory ModelContainer filled with mock data. body(content:context:)
applies that context to the previewed view, through modelContainer here, or through
environment for a custom store.
struct MockDataPreviewModifier: PreviewModifier {
static func makeSharedContext() throws -> ModelContainer {
let container = try ModelContainer(
for: Item.self,
configurations: ModelConfiguration(isStoredInMemoryOnly: true)
)
populateContainer(container)
return container
}
func body(content: Content, context: ModelContainer) -> some View {
content.modelContainer(context)
}
}
Apply it through the traits parameter:
#Preview(traits: .modifier(MockDataPreviewModifier())) {
ItemsView()
}
The shared context is cached
Xcode caches the value returned by makeSharedContext and reuses it across every preview with
the same trait. Expensive setup, such as seeding a data container, runs once instead of once
per preview.