Ana Sayfa / Blog / SwiftUI NavigationStack: eski NavigationView’dan geçmenin 7 farkı

SwiftUI NavigationStack: eski NavigationView’dan geçmenin 7 farkı

iOS 16'da gelen NavigationStack eski NavigationView'u değiştirdi. Migration sırasında karşılaşılan 7 önemli fark.

iOS 16’da NavigationStack geldi ve NavigationView deprecated oldu. iOS 17’de Apple bu migration’ı zorla ittirdi. 2 iOS uygulamamı NavigationView’dan NavigationStack’e migrate ettim, yollarda birkaç sürpriz çıktı.

Eğer hâlâ NavigationView kullanıyorsan veya migrate etmeyi düşünüyorsan, bu yazıda karşılaşacağın 7 önemli farkı anlatacağım.

1. NavigationStack path-based çalışıyor

Eski NavigationView’da navigation stack implicit’ti. NavigationLink’e basıyordun, SwiftUI arka planda yönetiyordu. State’i kontrol etmek zor.

NavigationStack’te explicit bir path binding’i var. Array veya NavigationPath. Navigation state’i manipüle etmek artık kod’la mümkün.

@State private var path = NavigationPath()

NavigationStack(path: $path) {
    // ...
}

Bu en büyük kazanç. Programmatic navigation, deep linking, state restore artık gerçekten çalışıyor.

2. navigationDestination() ile type-based routing

Eski sistemde her NavigationLink’e destination view tanımlamak gerekiyordu. Şimdi type bazlı routing var:

.navigationDestination(for: User.self) { user in
    UserDetailView(user: user)
}

Bu sayede path’e User object push edince otomatik destination açılıyor. Type-safe, refactor-friendly.

3. NavigationLink’in yeni syntax’ı

İki format var:

Value-based (önerilen):

NavigationLink(value: user) { UserRow(user: user) }

Destination-based (eski stil):

NavigationLink { UserDetailView(user: user) } label: { UserRow(user: user) }

Value-based olan navigationDestination ile çalışıyor. İkincisi hâlâ destekleniyor ama path tracking özelliklerini kaybediyorsun.

4. Programmatic navigation gerçekten işe yarıyor

Eski sistemde “login başarılı, ana ekrana yönlendir” için NavigationLink(isActive:) gibi hileler gerekiyordu. Bug’ları ve glitch’leri vardı.

NavigationStack ile:

@State private var path = NavigationPath()

func login() async {
    let user = await authService.login()
    path.append(user)
    // Veya daha derin: path.append(HomeRoute.dashboard)
}

Clean, predictable, test edilebilir.

5. Deep link handling artık native

Universal link veya URL scheme’den gelince, path’i set ederek istediğin ekrana gidersin:

func handleDeepLink(url: URL) {
    if url.pathComponents.contains("user") {
        let userId = url.lastPathComponent
        path = NavigationPath([User(id: userId)])
    }
}

Eski sistemde deep link için üç-dört farklı state binding kurmak gerekiyordu.

6. Backward compatibility dikkati

iOS 15 ve altını hâlâ destekliyorsan NavigationStack kullanamıyorsun (iOS 16+). Dual target kurmak zorundasın:

if #available(iOS 16.0, *) {
    NavigationStack { ContentView() }
} else {
    NavigationView { ContentView() }
}

Bu çirkin ama gerekli. iOS 15 kullanıcı oranınız %5’in altındaysa iOS 16 minimum’a çekmeyi düşünün.

7. Split view davranışı farklı

NavigationView’da .columns style ile iPad split view yapıyordun. NavigationStack tek column, split view için NavigationSplitView geldi:

NavigationSplitView {
    SidebarView()
} content: {
    ListView()
} detail: {
    DetailView()
}

iPhone’da tek column gibi davranıyor, iPad’de 3-column split. Migration ederken NavigationView’un kendi adaptive davranışını NavigationSplitView’e taşımak gerekti.

Migration stratejim

2 app migrate ederken şöyle ilerledim:

Adım 1: NavigationViewNavigationStack global replace. Hemen çalışıyor, path olmadan bile.

Adım 2: En sık kullanılan NavigationLink’leri value-based’e çevir. navigationDestination(for:) ekle.

Adım 3: Programmatic navigation ihtiyaçlarını path ile refactor et. Auth flow, deep link, tab-to-detail akışları.

Adım 4: iPad layout varsa NavigationSplitView’e geç.

Adım 5: Edge case’leri test et. Back gesture, swipe, state restoration.

Typical app için 1-2 günlük iş. Karmaşık navigation flow’lu app için 3-5 gün.

Karşılaştığım bug’lar

Birkaç yere dikkat:

  • iOS 16.0 bugs: NavigationStack iOS 16.0’da bazı animation glitch’leri vardı. iOS 16.1+’ta düzeldi. Minimum target’ı 16.1 yaptım.
  • Path’e non-Hashable değer koymak. NavigationPath Hashable istiyor. Custom type’ların Hashable conform etmesi gerekiyor.
  • Deeply nested navigation. 5+ seviye derin navigation path’te performance düşüyor. Pattern gözden geçirilmeli, belki modal sheet daha uygun.

NavigationSplitView hakkında ek notlar

iPad optimizasyonu ciddi. Eski NavigationView’un adaptive davranışı kısıtlıydı. NavigationSplitView:

  • .balanced, .prominentDetail, .doubleColumn style’ları var
  • Dynamic Type ve accessibility’ye yerleşik destek
  • State restoration otomatik

iPad app’i varsa NavigationSplitView migrasyonu bu kazanımları veriyor.

Sonuç

NavigationStack migration’ı 1-2 günlük iş. Kazanımlar: gerçek programmatic navigation, deep link support, type-safe routing, test edilebilirlik. iOS 16+ target’lı tüm projelerde yapmanız gereken bir geçiş.

iOS 15 desteği bırakmaya hazırsanız hiç ertelemeyin. Yeni navigation mental model’i daha sağlam ve SwiftUI’nın gelecek sürümlerinde daha fazla feature eklenecek bir temel.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç