Cómo usar PreviewModifier para crear Previews eficientes

Misael Cuevas
09 octubre, 20247min de lectura
PreviewModifier
es un protocolo que permite compartir datos entre previews, eliminando la duplicidad de código. Lo interesante de usar este protocolo es que el sistema de previews cachea los datos utilizados en un PreviewModifier
, permitiendo que sean reutilizados en las demás previews que lo implementen. Esto hace que tus previews sean mucho más eficientes.
PreviewModifier
es especialmente útil para mockear servicios en vistas que realizan peticiones a una API o acceso a bases de datos a través de dicho servicio. Esto asegura que la preview se cargue mucho más rápido.
App de ejemplo
Para demostrar las capacidades de PreviewModifier
, comienza con este simple ejemplo:
import Foundation@Observable final class SearchManager {private(set) var results = [String]()func update(_ results: [String]) {self.results = results}}
SearchManager
es una clase que administra una lista de resultados contenida en la variable results
y posee la función update
para reemplazar la lista de resultados. Además, la clase está marcada como @Observable
, lo que notifica a cualquier vista de SwiftUI que use SearchManager
sobre los cambios en results
.
Si quieres conocer más sobre
@Observable
, te recomiendo nuestro artículo sobre Observation en SwiftUI.
Para utilizar SearchManager
, crea la siguiente vista:
import SwiftUI// 1struct ContentView: View {// 2@Environment(SearchManager.self) private var searchManagervar body: some View {// 3List(searchManager.results, id: \.self) { result inText(result)}}}// 4#Preview {ContentView().environment(SearchManager())}
- Se define una vista llamada
ContentView
. - Se crea la variable
searchManager
para acceder aSearchManager
desde el entorno mediante@Environment
. - Se muestra una lista de resultados provenientes de
SearchManager
. - Se muestra una preview de
ContentView
en Xcode, asignando una instancia deSearchManager
al entorno de la vista.
Hasta este punto, la preview de ContentView
se muestra en blanco porque no se han agregado resultados con la función update
, por lo que la lista no tiene datos para mostrar.
Usando PreviewModifier
Una vez creado el ejemplo de la app, es hora de utilizar PreviewModifier
para configurar los datos de SearchManager
y usarlos en ContentView
. Crea la estructura PreviewSearchManager
que implemente el protocolo PreviewModifier
:
import SwiftUIstruct PreviewSearchManager: PreviewModifier {}
El protocolo requiere definir cuál será el tipo de datos a utilizar en las previews a través del alias Context
, en este caso SearchManager
. Esto permite usar protocolos de una manera genérica. Añade el siguiente código dentro de PreviewSearchManager
:
typealias Context = SearchManager
Con esto, ya puedes conformar las dos funciones del protocolo. La primera es makeSharedContext
, que sirve para crear el contexto que utilizarán las previews, además de cachear los datos. Esto significa que el contexto se creará una sola vez para todas las previews que usen este modificador. Su firma es la siguiente:
@MainActor static func makeSharedContext() async throws -> Self.Context
La segunda función es body
, que aplica el contexto con el parámetro context
al contenido de la vista mediante el parámetro content
. También puedes aplicar otros modificadores al contenido de la vista. Su firma es la siguiente:
@ViewBuilder @MainActor func body(content: Self.Content, context: Self.Context) -> Self.Body
Debajo del alias Context
, añade el siguiente código:
static func makeSharedContext() async throws -> SearchManager {// 1let results = ["Swift", "SwiftUI", "UIKit", "Combine", "MapKit"]// 2let searchManager = SearchManager()// 3searchManager.update(results)// 4return searchManager}func body(content: Content, context: SearchManager) -> some View {// 5content.environment(context)}
- Se define una lista de resultados.
- Se crea una instancia de
SearchManager
. - Se actualizan los resultados de
searchManager
con los valores predefinidos. - Se retorna la instancia de
SearchManager
con los valores establecidos. - Se incluye el
SearchManager
creado previamente en la funciónmakeSharedContext
a la vista.
Ahora estás listo para utilizar PreviewSearchManager
en la preview de ContentView
. Sustituye todo el código de la macro #Preview
con:
#Preview(traits: .modifier(PreviewSearchManager())) {ContentView()}
La diferencia con el antiguo #Preview
es que ahora se utiliza el parámetro traits
para aplicar el modificador PreviewSearchManager
, y se ha eliminado la asignación del entorno de la vista en ContentView
porque ya se está realizando en la función body
de PreviewSearchManager
.

Puedes simplificar aún más el uso del modificador dentro del parámetro traits
de la preview creando una variable estática en la extensión PreviewTrait
. Por ejemplo:
extension PreviewTrait where T == Preview.ViewTraits {@MainActor static var previewSearchManager: Self = .modifier(PreviewSearchManager())}
De esta manera, podrás usar tus previews de la siguiente forma:
#Preview(traits: .previewSearchManager) {ContentView()}
Si has seguido todas las instrucciones, tu código completo debería verse así:
import SwiftUI@Observable final class SearchManager {private(set) var results = [String]()func update(_ results: [String]) {self.results = results}}struct ContentView: View {@Environment(SearchManager.self) private var searchManagervar body: some View {List(searchManager.results, id: \.self) { result inText(result)}}}#Preview(traits: .previewSearchManager) {ContentView()}struct PreviewSearchManager: PreviewModifier {typealias Context = SearchManagerstatic func makeSharedContext() async throws -> SearchManager {let results = ["Swift", "SwiftUI", "UIKit", "Combine", "MapKit"]let searchManager = SearchManager()searchManager.update(results)return searchManager}func body(content: Content, context: SearchManager) -> some View {content.environment(context)}}extension PreviewTrait where T == Preview.ViewTraits {@MainActor static var previewSearchManager: Self = .modifier(PreviewSearchManager())}
PreviewModifier + SwiftData
Otro caso donde PreviewModifier
es muy útil es para mockear datos de SwiftData. Mira un ejemplo:
import SwiftData@Model final class SearchResult {var text: Stringinit(text: String) {self.text = text}static var mockData: [SearchResult] = [SearchResult(text: "Swift"),SearchResult(text: "SwiftUI"),SearchResult(text: "Apple"),]}
Se define el modelo SearchResult
, que representa los resultados de una búsqueda. Contiene una propiedad text
y una variable mockData
con datos de ejemplo.
import SwiftUIimport SwiftData// 1struct MockModelContainer: PreviewModifier {// 2typealias Context = ModelContainer// 3static func makeSharedContext() async throws -> ModelContainer {let configuration = ModelConfiguration(isStoredInMemoryOnly: true)let container = try ModelContainer(for: SearchResult.self, configurations: configuration)SearchResult.mockData.forEach { container.mainContext.insert($0) }return container}// 4func body(content: Content, context: ModelContainer) -> some View {content.modelContainer(context)}}// 5extension PreviewTrait where T == Preview.ViewTraits {@MainActor static var mockModelContainer: Self = .modifier(MockModelContainer())}
- Se define la estructura
MockModelContainer
, que conforma al protocoloPreviewModifier
para mockear los datos de SwiftData. - Se establece el
ModelContainer
como el contexto que utilizarán las previews. - Se crea una instancia de
ModelContainer
con una configuración específica, utilizando la variablemockData
deSearchResult
para insertar datos en el modelo. - Se aplica el contenedor de SwiftData proveniente del contexto
ModelContainer
al contenido de la vista. - Se simplifica el uso del modificador en los
traits
de la preview creando la variablemockModelContainer
en la extensiónPreviewTrait
.
import SwiftUIimport SwiftData// 1struct SearchResultsView: View {@Query private var searchResults: [SearchResult]var body: some View {List(searchResults) { result inText(result.text)}}}// 2#Preview(traits: .mockModelContainer) {SearchResultsView()}
- Se define la vista
SearchResultsView
, que muestra una lista de resultados de búsqueda a partir de una consulta al modeloSearchResult
. - Se define la preview de
SearchResultsView
con el mock del contenedor de SwiftData.

Si quieres conocer más sobre SwiftData te recomiendo nuestra guía esencial para comenzar con SwiftData.
Compatibilidad
PreviewModifier
puede utilizarse a partir de las siguientes versiones de los sistemas operativos de Apple:
- iOS 18.0+.
- iPadOS 18.0+.
- Mac Catalyst 13.0+.
- macOS 15.0+.
- tvOS 18.0+.
- visionOS 2.0+.
- watchOS 11.0+.