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
- NavigationView vs NavigationStack
- Temel Kullanim
- NavigationPath ile Programmatic Navigation
- Type-Safe Routing
- Deep Linking Entegrasyonu
- Coordinator Pattern
- Multi-Column Navigation (NavigationSplitView)
- State Restoration
- Best Practices
- Sonuc
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 SwiftUI2 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 in9 NavigationLink(fruit, value: fruit)10 }11 .navigationTitle("Meyveler")12 .navigationDestination(for: String.self) { fruit in13 FruitDetailView(name: fruit)14 }15 }16 }17}18 19struct FruitDetailView: View {20 let name: String21 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 in10 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 settings26 case blog(slug: String)27}3. NavigationPath ile Programmatic Navigation
NavigationPath, navigation stack'in durumunu yoneten type-erased bir koleksiyondur:
swift
1import SwiftUI2 3@Observable4class 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 in34 destinationView(for: route)35 }36 }37 .environment(router)38 }39 40 @ViewBuilder41 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 navigation54struct HomeView: View {55 @Environment(Router.self) private var router56 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 yapisi2enum AppRoute: Hashable {3 case home4 case productList(category: String)5 case productDetail(id: Int)6 case cart7 case checkout8 case orderConfirmation(orderId: String)9 case userProfile(userId: String)10 case editProfile11}12 13@Observable14class AppRouter {15 var path = NavigationPath()16 17 // Convenience methods18 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 git32 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 SwiftUI2 3@Observable4class DeepLinkHandler {5 var router: AppRouter6 7 init(router: AppRouter) {8 self.router = router9 }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 cevir16 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 break32 }33 }34}35 36// App'te kullanim37@main38struct MyApp: App {39 @State private var router = AppRouter()40 41 var body: some Scene {42 WindowGroup {43 RootView()44 .environment(router)45 .onOpenURL { url in46 DeepLinkHandler(router: router).handle(url: url)47 }48 }49 }50}6. Coordinator Pattern
swift
1// Protocol-based coordinator2protocol Coordinator {3 associatedtype Route: Hashable4 var path: NavigationPath { get set }5 func navigate(to route: Route)6}7 8@Observable9class AuthCoordinator: Coordinator {10 var path = NavigationPath()11 12 enum Route: Hashable {13 case login14 case register15 case forgotPassword16 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 yonlendir26 }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 in36 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 // Sidebar8 List(Category.allCases, selection: $selectedCategory) { category in9 Label(category.title, systemImage: category.icon)10 }11 .navigationTitle("Kategoriler")12 } content: {13 // Content14 if let category = selectedCategory {15 List(category.items, selection: $selectedItem) { item in16 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 // Detail26 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@Observable2class 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: data18 ) 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.

