Tüm Yazılar
KategoriSwiftUI
Okuma Süresi
22 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.344kelime

Kahveni hazırla - bu içerikli bir makale!

NavigationStack, NavigationPath, programmatic navigation, deep linking ve coordinator pattern ile modern SwiftUI navigation.

SwiftUI NavigationStack Deep Dive: Modern Navigation Rehberi

iOS 16 ile gelen NavigationStack, SwiftUI navigation'da cig atilmasini sagladi. NavigationView'in sinirlamalarini ortadan kaldiran bu yapi, programmatic navigation, type-safe routing ve deep linking destegi sunuyor. Bu rehberde NavigationStack'i tum detaylariyla inceleyecegiz.


Icindekiler


1. NavigationView vs NavigationStack

Karsilastirma Tablosu

Ozellik
NavigationView
NavigationStack
**Programmatic Nav**
Sinirli (isActive)
Tam (NavigationPath)
**Type Safety**
Zayif
Guclu
**Deep Linking**
Zor
Kolay
**Pop to Root**
Hack gerekli
path = []
**State Management**
Zor
NavigationPath
**Multi-level Push**
Her seviye ayri
Tek path yonetir
**Performance**
Orta
Iyi (lazy loading)
**Deprecation**
iOS 16+ deprecated
Aktif

2. Temel Kullanim

swift
1import SwiftUI
2 
3struct ContentView: View {
4 let fruits = ["Elma", "Armut", "Portakal", "Cilek", "Muz"]
5 
6 var body: some View {
7 NavigationStack {
8 List(fruits, id: \.self) { fruit in
9 NavigationLink(fruit, value: fruit)
10 }
11 .navigationTitle("Meyveler")
12 .navigationDestination(for: String.self) { fruit in
13 FruitDetailView(name: fruit)
14 }
15 }
16 }
17}
18 
19struct FruitDetailView: View {
20 let name: String
21 
22 var body: some View {
23 VStack(spacing: 20) {
24 Text(name)
25 .font(.largeTitle)
26 Text("Bu bir \(name) detay sayfasidir.")
27 .font(.body)
28 .foregroundStyle(.secondary)
29 }
30 .navigationTitle(name)
31 }
32}

Birden Fazla Tip icin Navigation Destination

swift
1struct AppView: View {
2 var body: some View {
3 NavigationStack {
4 VStack(spacing: 16) {
5 NavigationLink("Profil", value: Route.profile(userId: "123"))
6 NavigationLink("Ayarlar", value: Route.settings)
7 NavigationLink("Blog", value: Route.blog(slug: "swift-regex"))
8 }
9 .navigationDestination(for: Route.self) { route in
10 switch route {
11 case .profile(let userId):
12 ProfileView(userId: userId)
13 case .settings:
14 SettingsView()
15 case .blog(let slug):
16 BlogDetailView(slug: slug)
17 }
18 }
19 }
20 }
21}
22 
23enum Route: Hashable {
24 case profile(userId: String)
25 case settings
26 case blog(slug: String)
27}

3. NavigationPath ile Programmatic Navigation

NavigationPath, navigation stack'in durumunu yoneten type-erased bir koleksiyondur:

swift
1import SwiftUI
2 
3@Observable
4class Router {
5 var path = NavigationPath()
6 
7 func navigate(to route: Route) {
8 path.append(route)
9 }
10 
11 func popToRoot() {
12 path = NavigationPath()
13 }
14 
15 func pop() {
16 if !path.isEmpty {
17 path.removeLast()
18 }
19 }
20 
21 func pop(count: Int) {
22 let removeCount = min(count, path.count)
23 path.removeLast(removeCount)
24 }
25}
26 
27struct RootView: View {
28 @State private var router = Router()
29 
30 var body: some View {
31 NavigationStack(path: $router.path) {
32 HomeView()
33 .navigationDestination(for: Route.self) { route in
34 destinationView(for: route)
35 }
36 }
37 .environment(router)
38 }
39 
40 @ViewBuilder
41 func destinationView(for route: Route) -> some View {
42 switch route {
43 case .profile(let userId):
44 ProfileView(userId: userId)
45 case .settings:
46 SettingsView()
47 case .blog(let slug):
48 BlogDetailView(slug: slug)
49 }
50 }
51}
52 
53// Herhangi bir child view'den navigation
54struct HomeView: View {
55 @Environment(Router.self) private var router
56 
57 var body: some View {
58 VStack {
59 Button("Profile Git") {
60 router.navigate(to: .profile(userId: "123"))
61 }
62 Button("Ayarlar") {
63 router.navigate(to: .settings)
64 }
65 }
66 }
67}

4. Type-Safe Routing

swift
1// Modullere ayrilmis route yapisi
2enum AppRoute: Hashable {
3 case home
4 case productList(category: String)
5 case productDetail(id: Int)
6 case cart
7 case checkout
8 case orderConfirmation(orderId: String)
9 case userProfile(userId: String)
10 case editProfile
11}
12 
13@Observable
14class AppRouter {
15 var path = NavigationPath()
16 
17 // Convenience methods
18 func showProduct(_ id: Int) {
19 path.append(AppRoute.productDetail(id: id))
20 }
21 
22 func showCart() {
23 path.append(AppRoute.cart)
24 }
25 
26 func startCheckout() {
27 path.append(AppRoute.checkout)
28 }
29 
30 func completeOrder(orderId: String) {
31 // Checkout ve cart'i kaldir, confirmation'a git
32 while path.count > 1 {
33 path.removeLast()
34 }
35 path.append(AppRoute.orderConfirmation(orderId: orderId))
36 }
37 
38 func popToRoot() {
39 path = NavigationPath()
40 }
41}

5. Deep Linking Entegrasyonu

swift
1import SwiftUI
2 
3@Observable
4class DeepLinkHandler {
5 var router: AppRouter
6 
7 init(router: AppRouter) {
8 self.router = router
9 }
10 
11 func handle(url: URL) {
12 guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
13 let host = components.host else { return }
14 
15 // URL'i route'a cevir
16 router.popToRoot()
17 
18 switch host {
19 case "product":
20 if let idStr = components.queryItems?.first(where: { $0.name == "id" })?.value,
21 let id = Int(idStr) {
22 router.showProduct(id)
23 }
24 case "profile":
25 if let userId = components.queryItems?.first(where: { $0.name == "user" })?.value {
26 router.path.append(AppRoute.userProfile(userId: userId))
27 }
28 case "cart":
29 router.showCart()
30 default:
31 break
32 }
33 }
34}
35 
36// App'te kullanim
37@main
38struct MyApp: App {
39 @State private var router = AppRouter()
40 
41 var body: some Scene {
42 WindowGroup {
43 RootView()
44 .environment(router)
45 .onOpenURL { url in
46 DeepLinkHandler(router: router).handle(url: url)
47 }
48 }
49 }
50}

6. Coordinator Pattern

swift
1// Protocol-based coordinator
2protocol Coordinator {
3 associatedtype Route: Hashable
4 var path: NavigationPath { get set }
5 func navigate(to route: Route)
6}
7 
8@Observable
9class AuthCoordinator: Coordinator {
10 var path = NavigationPath()
11 
12 enum Route: Hashable {
13 case login
14 case register
15 case forgotPassword
16 case otpVerification(phone: String)
17 }
18 
19 func navigate(to route: Route) {
20 path.append(route)
21 }
22 
23 func completeAuth() {
24 path = NavigationPath()
25 // Ana sayfaya yonlendir
26 }
27}
28 
29struct AuthFlow: View {
30 @State private var coordinator = AuthCoordinator()
31 
32 var body: some View {
33 NavigationStack(path: $coordinator.path) {
34 LoginView()
35 .navigationDestination(for: AuthCoordinator.Route.self) { route in
36 switch route {
37 case .login:
38 LoginView()
39 case .register:
40 RegisterView()
41 case .forgotPassword:
42 ForgotPasswordView()
43 case .otpVerification(let phone):
44 OTPView(phone: phone)
45 }
46 }
47 }
48 .environment(coordinator)
49 }
50}

7. Multi-Column Navigation

swift
1struct iPadNavigation: View {
2 @State private var selectedCategory: Category?
3 @State private var selectedItem: Item?
4 
5 var body: some View {
6 NavigationSplitView {
7 // Sidebar
8 List(Category.allCases, selection: $selectedCategory) { category in
9 Label(category.title, systemImage: category.icon)
10 }
11 .navigationTitle("Kategoriler")
12 } content: {
13 // Content
14 if let category = selectedCategory {
15 List(category.items, selection: $selectedItem) { item in
16 Text(item.name)
17 }
18 .navigationTitle(category.title)
19 } else {
20 ContentUnavailableView("Kategori Secin",
21 systemImage: "sidebar.left",
22 description: Text("Sol menuden bir kategori secin"))
23 }
24 } detail: {
25 // Detail
26 if let item = selectedItem {
27 ItemDetailView(item: item)
28 } else {
29 ContentUnavailableView("Oge Secin",
30 systemImage: "doc.text",
31 description: Text("Listeden bir oge secin"))
32 }
33 }
34 .navigationSplitViewStyle(.balanced)
35 }
36}

8. State Restoration

swift
1@Observable
2class PersistableRouter {
3 var path = NavigationPath()
4 
5 private let key = "navigation_state"
6 
7 func save() {
8 guard let data = path.codable else { return }
9 if let encoded = try? JSONEncoder().encode(data) {
10 UserDefaults.standard.set(encoded, forKey: key)
11 }
12 }
13 
14 func restore() {
15 guard let data = UserDefaults.standard.data(forKey: key),
16 let decoded = try? JSONDecoder().decode(
17 NavigationPath.CodableRepresentation.self, from: data
18 ) else { return }
19 path = NavigationPath(decoded)
20 }
21}

9. Best Practices

Kural
Aciklama
**Tek Router kullanin**
Uygulama genelinde tek bir router yeterli
**Route enum kullanin**
String-based routing yerine type-safe enum
**Deep link'leri test edin**
Her route icin URL handler testi yazin
**State restoration ekleyin**
Kullanici deneyimini iyilestirir
**popToRoot kolayligi**
Uzun stack'leri temizlemek icin

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Tebrikler! Bu yaziyi sonuna kadar okudugun icin sana ozel bir hediyem var:

10. Sonuc

NavigationStack, SwiftUI'da navigation'in nasil yapilmasi gerektiginin cevabi. NavigationView'in sinirlamalarini tamamen ortadan kaldiriyor ve modern iOS uygulamalari icin vazgecilmez bir arac. Programmatic navigation, deep linking ve state restoration ozellikleri ile production-ready uygulamalar gelistirmek artik cok daha kolay.

Etiketler

#SwiftUI#Navigation#NavigationStack#Deep Linking#iOS#Architecture
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz