SwiftUI’nin Preview özelliği ilk gördüğümde “oyun değiştirici” dedim. Canvas’ta instant render, real-time update, farklı environment’ta paralel preview. UIKit’teki Storyboard + IB deneyiminden sonra özgürlük hissi.
4 yıl, 12 uygulama sonrası görüşüm karışık. Preview çok güçlü ama bazı pitfall’lar var. Bu yazıda neyi sevdiğimi, neye dikkat ettiğimi paylaşıyorum.
Preview’in güçlü yanları
Instant visual feedback. Code change’i 1-2 saniyede canvas’ta görüyorsun. Simulator boot etmeden, build-run döngüsü olmadan.
Parametric preview. Farklı data set, farklı color scheme, farklı device size paralel görülüyor.
#Preview("Small") {
ProductCard(product: .small)
}
#Preview("Large") {
ProductCard(product: .large)
}
#Preview("Dark Mode") {
ProductCard(product: .standard)
.preferredColorScheme(.dark)
}Bir component’ı 5 farklı state’te yan yana inceliyorsun.
Environment injection. Locale, color scheme, accessibility setting, size class’ı preview’e inject edip iOS’un tüm o edge case’lerini baştan görüyorsun.
#Preview("Arabic") {
ContentView()
.environment(\.locale, Locale(identifier: "ar"))
.environment(\.layoutDirection, .rightToLeft)
}Arapça / RTL layout production’da bug’lı mı diye test için simulator switch yerine preview ile görüyorsun.
Preview’in zayıf yanları
Real device ≠ Preview. Preview SwiftUI render engine’ı ile çalışıyor, ama simulator veya real device’da render farklı olabiliyor. Bazı animation’lar, bazı gesture’lar, bazı system interaction’lar preview’de çalışmıyor.
Preview’de çalışan UI real device’da kırılabilir. Production’a geçmeden mutlaka simulator + real device test.
Performance illusion. Preview canvas rendering optimized ama real device performance değil. 1000 item’lık LazyVStack preview’de akıcı görünüyor ama iPhone 12’de kasiyor. Performance real device’da ölçülmeli.
Mock data bias. Preview için hazırladığın mock data her zaman “happy path”. Real data’da edge case (çok uzun string, empty array, nil field) olduğunda layout kırılıyor.
Preview’e edge case’leri de eklemek disiplinli yaklaşım:
#Preview("Empty") {
ListView(items: [])
}
#Preview("Single") {
ListView(items: [.mock])
}
#Preview("100 items") {
ListView(items: (0..<100).map { .mock(id: $0) })
}
#Preview("Long title") {
ListView(items: [.mock(title: String(repeating: "a", count: 500))])
}Preview neden crash ediyor
Preview en frustre edici tarafı: random “Preview paused” veya “Failed to build preview” mesajları. Çözemediğin durumlar oluyor.
Yaygın nedenler:
1. External dependency. Preview içinde API call, file access, CoreData context varsa preview stuck. Network catch edip mock data fallback kullan.
2. Environment object eksik. Parent view’in verdiği environment object preview’de yok. Hepsini preview’de manuel inject et:
#Preview {
ChildView()
.environmentObject(AppState())
.environment(\.modelContext, PreviewData.modelContext)
}3. Singleton / global state. UserDefaults, FileManager, Keychain gibi global state’e dokunan kod preview’de yan etki yapıyor. Dependency injection ile protokol’e bağla, preview’de mock inject et.
4. Async work. Task {} içinde long-running async iş preview’i lock’lıyor. #if DEBUG + preview-only fast-path.
Mock data disciplini
12 app’te öğrendiğim en değerli pratik: her model type’ının bir mock veya preview static property’si olsun:
extension User {
static let preview = User(
id: UUID(),
name: "Test User",
email: "test@example.com",
createdAt: Date()
)
}
extension Order {
static let preview = Order(
id: "ord_001",
user: .preview,
total: 15000,
items: [.preview]
)
}Preview’de:
#Preview {
OrderDetailView(order: .preview)
}Tek satır. Mock data tutarlı, reuse ediliyor, bir yerde maintain ediliyor.
PreviewProvider vs #Preview macro
Xcode 15+ #Preview macro’su PreviewProvider protocol’ünün yerini aldı. Daha sade:
// Old (still works)
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// New
#Preview {
ContentView()
}Yeni projede #Preview tercih, mevcut projede migration acil değil.
Interactive preview
iOS 17+ preview’de user interaction mümkün. Button’a basıyor, sheet açılıyor, state değişiyor.
Önceki preview’ler statik render idi. Şimdi navigate edip farklı screen’leri canvas’ta test ediyorsun. Huge productivity.
Preview’i test olarak kullanma tuzak
Preview’i test yerine kullanmak gördüğüm anti-pattern. “Bak, preview’de OK, çalışıyor” denip snapshot test yazılmamak.
Preview tasarım aracı, test değil. Regression detection yok, CI’da çalışmıyor. Snapshot test ayrı kurulmalı (swift-snapshot-testing kütüphanesi güzel).
Environment / trait customization
Preview’de iOS system setting’leri simulate:
#Preview("Large Text") {
ContentView()
.environment(\.dynamicTypeSize, .accessibility3)
}
#Preview("Voice Over") {
ContentView()
.environment(\.accessibilityEnabled, true)
}
#Preview("Right to Left") {
ContentView()
.environment(\.layoutDirection, .rightToLeft)
}Accessibility, large text, RTL gibi test etmezsen production’a kaçan bug’lar preview’de yakalanıyor.
Multi-device preview
Preview modifier’larla farklı device:
#Preview("iPhone 15") {
ContentView()
}
#Preview("iPad Pro") {
ContentView()
.previewDevice("iPad Pro (12.9-inch)")
}
#Preview("Apple Watch") {
ContentView()
.previewDevice("Apple Watch Ultra 2")
}Universal app yazıyorsan her device’da nasıl görünüyor paralel göster.
Canvas performance tuning
Büyük view hierarchy preview’de yavaşlarsa:
PreviewProvider‘ıLazyVStackiçindeki büyük listenin dışında tut, wrapper view preview et- Complex view’i render etmeden önce
Color.grayplaceholder kullan - Animation’ları preview’de
.transaction { $0.animation = nil }ile disable - Async image loading yerine placeholder image
Gerçek üretimde Preview workflow’um
- Feature tasarlarken önce preview odaklı yazıyorum
- Mock data hazırlıyorum (edge case dahil)
- Preview’de tüm state’lerde OK olana kadar iterate
- Simulator’da smoke test
- Real device’da full feature test
- Snapshot test (optional, kritik UI için)
Bu workflow’da preview’i %60 ilk pass için kullanıyorum, %40 real device test bulguları ile düzeltme yapıyorum. Preview zamandan çok tasarruf sağlıyor ama son söz real device’ın.
Son tavsiye
SwiftUI Preview güçlü bir tool. Ama illusion boyutu da var. Preview’e blind güvenmek bug’lı production’a yol açıyor.
Akıllı kullanım:
– Mock data’ya edge case’ler dahil et
– Environment injection’larla system setting’leri simulate et
– Real device testing’i skip etme
– Snapshot test ile regression protect et
Bu disiplinle preview productivity’yi 2x arttırıyor. Disiplinsiz kullanımda “preview’de OK idi” dediğin bug’lar production’a düşüyor.