SwiftUI-превью часто начинаются с простого кода. Но со временем в них появляется повторяющаяся настройка. Нужно создать временное хранилище данных, заполнить его тестовыми записями, передать это хранилище в представление. И так для каждого превью.
PreviewModifier решает эту проблему. Он позволяет вынести настройку в одно место и переиспользовать ее в нескольких превью.
Что такое PreviewModifier:
PreviewModifier - это API для превью, представленный в Xcode 16. Он разделяет настройку зависимостей и объявление
#Preview.Модификатор может создавать общий контекст и применять его к содержимому превью:
struct SampleDataPreviewModifier: PreviewModifier {
static func makeSharedContext() throws -> ModelContainer {
let configuration = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try ModelContainer(for: Item.self, configurations: configuration)
let context = container.mainContext
context.insert(Item(title: "Milk"))
context.insert(Item(title: "Coffee"))
context.insert(Item(title: "Bread"))
try context.save()
return container
}
func body(content: Content, context: ModelContainer) -> some View {
content.modelContainer(context)
}
}
Превью остаётся сфокусированным на представлении:
#Preview(traits: .modifier(SampleDataPreviewModifier())) {
ShoppingListView()
}
Создание общего контекста:
Модификатор, которому нужны общие данные, реализует метод makeSharedContext(). Он создает значение, которое потом применяется к превью.
Контекст может содержать временное хранилище данных, мок-сервисы, тестовые данные, контейнер зависимостей, данные из локального файла. Метод поддерживает асинхронную работу и распространение ошибок.
Система превью кеширует возвращенный контекст и передает его каждому превью, использующему модификатор того же типа. Это избавляет от повторной настройки.
Контекст общий, поэтому он подходит для зависимостей и начальных данных, но не для изменяемого состояния конкретного превью.
Применение контекста:
SwiftUI передает подготовленный контекст в метод body модификатора.
func body(content: Content, context: AppDependencies) -> some View {
content
.environment(context.session)
.environment(context.apiClient)
}
content - это представление, возвращенное
#Preview. Метод может применять модификаторы, внедрять значения окружения, подключать контейнер.Когда несколько зависимостей связаны, отдельный тип контекста помогает их организовать.
struct AppDependencies {
let session: SessionModel
let apiClient: MockAPIClient
}
Переиспользуемый трейт:
Использовать .modifier(...) напрямую можно. Но кастомный трейт делает повторяющиеся превью короче.
extension PreviewTrait where T == Preview.ViewTraits {
@MainActor
static var sampleData: Self {
.modifier(SampleDataPreviewModifier())
}
}
Теперь модификатор применяется по имени:
#Preview(traits: .sampleData) {
ShoppingListView()
}
В большом проекте можно определить несколько сфокусированных окружений:
#Preview(traits: .emptyDatabase) {
ShoppingListView()
}
#Preview(traits: .authenticatedUser) {
ProfileView()
}
Каждый трейт должен описывать конкретный сценарий, а не становиться единым контейнером для всех зависимостей приложения.
🔗 Читать подробнее
💡 Вывод:
PreviewModifier создает четкую границу между настройкой превью и его содержимым. Он уменьшает дублирование конфигурации, поддерживает асинхронную подготовку и позволяет системе превью делить дорогой контекст между несколькими превью.
Это особенно полезно для SwiftData, моковых зависимостей и единообразных окружений превью в большом проекте. Вместо повторения одного и того же кода в каждом превью вы настраиваете контекст один раз и переиспользуете его.
Подписаться на канал:
➡️ Telegram | Max