Почему Previews всё ещё ломаются
Apple значительно улучшила Previews в последних версиях Xcode, но фундаментальная проблема никуда не делась: Preview компилирует ваш код в отдельном процессе с ограниченным окружением. Всё что зависит от внешних ресурсов, сети, CoreData или сложной инициализации — потенциальный источник боли.
1. Всегда используйте PreviewProvider с моковыми данными
Никогда не передавайте в Preview реальные зависимости. Создайте протокол и моковую реализацию:
protocol UserServiceProtocol {
func fetchUsers() async throws -> [User]
}
struct MockUserService: UserServiceProtocol {
func fetchUsers() async throws -> [User] {
[.init(name: "Иван"), .init(name: "Мария")]
}
}
#Preview {
UserListView(service: MockUserService())
}
2. Изолируйте Preview от CoreData
CoreData — главный убийца Previews. Используйте in-memory store специально для них:
extension PersistenceController {
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let context = result.container.viewContext
// Добавляем тестовые данные
for i in 0..<5 {
let item = Item(context: context)
item.title = "Item \(i)"
}
try? context.save()
return result
}()
}
3. #Preview макрос вместо PreviewProvider
Начиная с Xcode 15 можно использовать краткий синтаксис. Он компилируется быстрее и меньше кода:
// Старый способ
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// Новый способ
#Preview {
ContentView()
}
4. Несколько Preview в одном файле
Показывайте разные состояния компонента сразу — светлая/тёмная тема, разные локали, edge cases:
#Preview("Светлая тема") {
ButtonView(title: "Купить")
}
#Preview("Тёмная тема") {
ButtonView(title: "Купить")
.preferredColorScheme(.dark)
}
#Preview("Длинный текст") {
ButtonView(title: "Очень длинное название кнопки которое не влезает")
}
5. Принудительный сброс Preview
Когда Preview завис и не реагирует — не перезапускайте Xcode. Попробуйте сначала:
- Нажать ⌘ + Option + P — обновить Preview
- В меню Debug → Restart Preview Extension
- Закрыть и открыть Preview панель
Совет: Если Preview регулярно зависает на конкретном файле, скорее всего там есть синхронная операция которая блокирует поток. Ищите force try, force unwrap или тяжёлую инициализацию на верхнем уровне.
6. Simulators vs Preview
Preview не заменяет симулятор. Анимации, жесты, навигация — всё это нужно тестировать на симуляторе. Preview идеален для итерации над внешним видом компонентов.
7. Сборка в Preview-режиме
Добавьте флаг в код который меняет поведение при Preview:
var isPreview: Bool {
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
func setupAnalytics() {
guard !isPreview else { return }
// Реальная инициализация аналитики
}