# SwiftUI NavigationStack mülakat soruları: Navigasyon kalıpları 2026 > iOS mülakatları için NavigationStack, NavigationPath ve modern SwiftUI navigasyon kalıpları üzerine temel sorularla hazırlık. - Published: 2026-02-24 - Updated: 2026-04-29 - Author: SharpSkill - Tags: swiftui, ios, navigation, interview, navigationstack - Reading time: 12 min --- Navigasyon, herhangi bir iOS uygulamasının temel direklerinden birini oluşturur. iOS 16'dan itibaren `NavigationStack`, `NavigationView`'in yerini alır ve navigasyon yığını üzerinde tam programatik kontrol sunar. İşe alımcılar bu kavramların hakimiyetini teknik mülakatlar sırasında düzenli olarak ölçer. > **Rehber yapısı** > > Her soru, ayrıntılı yanıt ve çalışan kod ile birlikte gerçek bir teknik mülakatın formatını yansıtır. Kavramlar temelden ileri seviyeye doğru ilerler. ## NavigationStack temelleri ### Soru 1: NavigationView ile NavigationStack arasındaki fark nedir? `NavigationView` (iOS 16'dan beri kullanım dışı), iç içe geçmiş `NavigationLink`'lere dayalı örtük bir navigasyon oluşturuyordu. `NavigationStack`, açık ve programatik olarak değiştirilebilen bir navigasyon yığını ile bildirimsel bir yaklaşım sunar. ```swift // NavigationComparison.swift // ❌ Old pattern with NavigationView (deprecated) struct OldNavigation: View { var body: some View { NavigationView { NavigationLink("Details", destination: DetailView()) } } } // ✅ New pattern with NavigationStack struct ModernNavigation: View { // Navigation stack is explicit and controllable @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { List { // NavigationLink with typed value NavigationLink("User 1", value: User(id: 1, name: "Alice")) NavigationLink("User 2", value: User(id: 2, name: "Bob")) } // Destination defined by value type .navigationDestination(for: User.self) { user in UserDetailView(user: user) } } } } ``` En büyük avantaj, link bildiriminin hedefinden ayrılmasıdır; bu da merkezi ve test edilebilir bir navigasyon olanaklı kılar. ### Soru 2: NavigationPath nasıl çalışır? `NavigationPath`, navigasyon değerlerini saklayan, türü silinmiş bir konteynerdir. Ekranların kesin türlerini bilmeden yığını yönetmeyi sağlar ve bunu yaparken derleme zamanı tip güvenliğini korur. ```swift // NavigationPathBasics.swift struct ContentView: View { // NavigationPath can contain different Hashable types @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { VStack(spacing: 20) { Button("View user profile") { // Adds a User to the stack path.append(User(id: 1, name: "Alice")) } Button("View settings") { // Adds a Settings enum to the stack path.append(SettingsRoute.notifications) } Button("Back to root") { // Clears the entire stack path.removeLast(path.count) } } .navigationDestination(for: User.self) { user in UserDetailView(user: user) } .navigationDestination(for: SettingsRoute.self) { route in SettingsView(route: route) } } } } // Types must be Hashable struct User: Hashable { let id: Int let name: String } enum SettingsRoute: Hashable { case notifications case privacy case account } ``` > **Türü silinmiş ama tip güvenli** > > `NavigationPath` dahili olarak tip silme kullanır, ancak `navigationDestination` aracılığıyla tipleri derleme zamanında doğrular. Karşılığı bulunmayan bir değer, sessiz bir çalışma zamanı hatasına yol açar. ### Soru 3: Tam programatik bir navigasyon nasıl uygulanır? Programatik navigasyon, kullanıcı etkileşimi olmadan kodun herhangi bir yerinden yığını kontrol etmeyi sağlar. Deep link'ler, kimlik doğrulama sonrası yönlendirmeler veya çok adımlı akışlar için vazgeçilmezdir. ```swift // ProgrammaticNavigation.swift // Centralized router to manage navigation @Observable class NavigationRouter { var path = NavigationPath() // Navigate to a specific screen func navigateTo(_ destination: AppRoute) { path.append(destination) } // Go back one level func goBack() { guard !path.isEmpty else { return } path.removeLast() } // Return to root func popToRoot() { path.removeLast(path.count) } // Navigate to a complete stack (deep link) func navigateToPath(_ routes: [AppRoute]) { popToRoot() for route in routes { path.append(route) } } } // Enum defining all app routes enum AppRoute: Hashable { case userList case userDetail(userId: Int) case userEdit(userId: Int) case settings case settingsDetail(SettingsSection) } enum SettingsSection: String, Hashable { case notifications, privacy, account } // Usage in main view struct MainView: View { @State private var router = NavigationRouter() var body: some View { NavigationStack(path: $router.path) { HomeView() .navigationDestination(for: AppRoute.self) { route in destinationView(for: route) } } .environment(router) } @ViewBuilder private func destinationView(for route: AppRoute) -> some View { switch route { case .userList: UserListView() case .userDetail(let userId): UserDetailView(userId: userId) case .userEdit(let userId): UserEditView(userId: userId) case .settings: SettingsView() case .settingsDetail(let section): SettingsDetailView(section: section) } } } ``` Bu kalıp tüm navigasyon mantığını merkezileştirir, birim testlerini ve bakımı kolaylaştırır. ## İleri navigasyon kalıpları ### Soru 4: NavigationStack ile deep link'ler nasıl uygulanır? Deep link'ler, harici bir URL'den uygulamayı doğrudan belirli bir ekrana açmayı sağlar. `NavigationStack` ile yığın programatik olarak yeniden oluşturulabilir. ```swift // DeepLinkHandler.swift @Observable class DeepLinkHandler { var router: NavigationRouter init(router: NavigationRouter) { self.router = router } // Parse a URL and navigate to destination func handle(url: URL) { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true), let host = components.host else { return } // Build navigation stack according to URL let routes = parseRoutes(host: host, path: components.path, queryItems: components.queryItems) router.navigateToPath(routes) } private func parseRoutes(host: String, path: String, queryItems: [URLQueryItem]?) -> [AppRoute] { // myapp://users/42/edit → [.userList, .userDetail(42), .userEdit(42)] switch host { case "users": return parseUserPath(path) case "settings": return parseSettingsPath(path) default: return [] } } private func parseUserPath(_ path: String) -> [AppRoute] { let segments = path.split(separator: "/").map(String.init) var routes: [AppRoute] = [.userList] if let userIdString = segments.first, let userId = Int(userIdString) { routes.append(.userDetail(userId: userId)) // /users/42/edit if segments.count > 1 && segments[1] == "edit" { routes.append(.userEdit(userId: userId)) } } return routes } private func parseSettingsPath(_ path: String) -> [AppRoute] { var routes: [AppRoute] = [.settings] let segments = path.split(separator: "/").map(String.init) if let sectionString = segments.first, let section = SettingsSection(rawValue: sectionString) { routes.append(.settingsDetail(section)) } return routes } } // In the main App @main struct MyApp: App { @State private var router = NavigationRouter() @State private var deepLinkHandler: DeepLinkHandler? var body: some Scene { WindowGroup { MainView() .environment(router) .onOpenURL { url in // Handle deep links deepLinkHandler?.handle(url: url) } .onAppear { deepLinkHandler = DeepLinkHandler(router: router) } } } } ``` ### Soru 5: Navigasyon durumu nasıl kalıcı hale getirilir ve geri yüklenir? Navigasyon durumunun kalıcı hale getirilmesi, uygulama yeniden başlatıldığında kullanıcının konumunu geri yüklemeyi sağlar. `NavigationPath`, serileştirme için `Codable` desteği sunar. ```swift // NavigationPersistence.swift // Extension to make NavigationPath persistable extension NavigationPath { // Encode path to Data func encoded() -> Data? { guard let representation = self.codable else { return nil } return try? JSONEncoder().encode(representation) } // Decode from Data static func decoded(from data: Data) -> NavigationPath? { guard let representation = try? JSONDecoder().decode( NavigationPath.CodableRepresentation.self, from: data ) else { return nil } return NavigationPath(representation) } } // Router with persistence @Observable class PersistentNavigationRouter { var path: NavigationPath { didSet { saveState() } } private let storageKey = "navigation_path" init() { // Restore state at startup if let data = UserDefaults.standard.data(forKey: storageKey), let restored = NavigationPath.decoded(from: data) { self.path = restored } else { self.path = NavigationPath() } } private func saveState() { if let data = path.encoded() { UserDefaults.standard.set(data, forKey: storageKey) } } func clearPersistedState() { UserDefaults.standard.removeObject(forKey: storageKey) } } ``` > **Codable uyumluluğu** > > `NavigationPath.codable`'ın çalışması için yola eklenen tüm tiplerin `Hashable`'a ek olarak `Codable` olması gerekir. Aksi halde `codable` özelliği `nil` döner. ### Soru 6: Kimlik doğrulama akışlarıyla navigasyon nasıl yönetilir? Kimlik doğrulama akışları genellikle giriş sonrası korumalı bir ekrana yönlendirme veya çıkış sonrası giriş ekranına dönüş gerektirir. Aşağıdaki kalıp bu geçişleri temiz biçimde yönetir. ```swift // AuthNavigationFlow.swift enum AuthState { case unauthenticated case authenticated(User) } @Observable class AuthManager { var state: AuthState = .unauthenticated var pendingDeepLink: URL? func login(email: String, password: String) async throws { // Simulated authentication let user = try await AuthService.shared.login(email: email, password: password) state = .authenticated(user) } func logout() { state = .unauthenticated } } struct RootView: View { @State private var authManager = AuthManager() @State private var router = NavigationRouter() var body: some View { Group { switch authManager.state { case .unauthenticated: // Separate navigation stack for auth AuthNavigationStack(authManager: authManager) case .authenticated: // Main app stack MainNavigationStack(router: router, authManager: authManager) } } .onChange(of: authManager.state) { oldState, newState in handleAuthStateChange(from: oldState, to: newState) } } private func handleAuthStateChange(from oldState: AuthState, to newState: AuthState) { switch (oldState, newState) { case (.unauthenticated, .authenticated): // Login successful: process pending deep link if let pendingURL = authManager.pendingDeepLink { DeepLinkHandler(router: router).handle(url: pendingURL) authManager.pendingDeepLink = nil } case (.authenticated, .unauthenticated): // Logout: reset navigation router.popToRoot() default: break } } } struct AuthNavigationStack: View { let authManager: AuthManager @State private var authPath = NavigationPath() var body: some View { NavigationStack(path: $authPath) { LoginView(authManager: authManager) .navigationDestination(for: AuthRoute.self) { route in switch route { case .register: RegisterView(authManager: authManager) case .forgotPassword: ForgotPasswordView() } } } } } enum AuthRoute: Hashable { case register case forgotPassword } ``` ### Soru 7: NavigationStack ile modal navigasyon nasıl uygulanır? Modal'lar ve sheet'ler kendi navigasyon bağlamlarını gerektirir. `NavigationStack`'i modal sunumlarla birleştirmek ayrı bir durum yönetimi gerektirir. ```swift // ModalNavigation.swift struct ParentView: View { @State private var mainPath = NavigationPath() @State private var showSettings = false @State private var showUserProfile: User? var body: some View { NavigationStack(path: $mainPath) { ContentView() .toolbar { Button("Settings") { showSettings = true } } .navigationDestination(for: MainRoute.self) { route in MainRouteView(route: route) } } // Sheet with its own NavigationStack .sheet(isPresented: $showSettings) { SettingsSheet() } // Conditional sheet based on item .sheet(item: $showUserProfile) { user in UserProfileSheet(user: user) } } } // Each sheet has its own NavigationStack struct SettingsSheet: View { @Environment(\.dismiss) private var dismiss @State private var settingsPath = NavigationPath() var body: some View { NavigationStack(path: $settingsPath) { SettingsListView() .navigationTitle("Settings") .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Close") { dismiss() } } } .navigationDestination(for: SettingsSection.self) { section in SettingsDetailView(section: section) } } } } // Extension to make User identifiable for sheet(item:) extension User: Identifiable {} ``` ## Durum yönetimi ve test edilebilirlik ### Soru 8: Navigasyon için birim testler nasıl yazılır? Test edilebilirlik, `NavigationStack`'in önemli avantajlarından biridir. Router izole edilerek birim testler navigasyon mantığını UI olmadan doğrular. ```swift // NavigationTests.swift // Protocol to abstract the router protocol NavigationRouterProtocol { var pathCount: Int { get } func navigateTo(_ destination: AppRoute) func goBack() func popToRoot() } // Concrete implementation @Observable class AppNavigationRouter: NavigationRouterProtocol { var path = NavigationPath() var pathCount: Int { path.count } func navigateTo(_ destination: AppRoute) { path.append(destination) } func goBack() { guard !path.isEmpty else { return } path.removeLast() } func popToRoot() { path.removeLast(path.count) } } // Unit tests import XCTest final class NavigationRouterTests: XCTestCase { var router: AppNavigationRouter! override func setUp() { router = AppNavigationRouter() } func testNavigateToAddsToPath() { // Given XCTAssertEqual(router.pathCount, 0) // When router.navigateTo(.userDetail(userId: 42)) // Then XCTAssertEqual(router.pathCount, 1) } func testGoBackRemovesLastItem() { // Given router.navigateTo(.userList) router.navigateTo(.userDetail(userId: 1)) XCTAssertEqual(router.pathCount, 2) // When router.goBack() // Then XCTAssertEqual(router.pathCount, 1) } func testPopToRootClearsPath() { // Given router.navigateTo(.userList) router.navigateTo(.userDetail(userId: 1)) router.navigateTo(.userEdit(userId: 1)) XCTAssertEqual(router.pathCount, 3) // When router.popToRoot() // Then XCTAssertEqual(router.pathCount, 0) } func testGoBackOnEmptyPathDoesNothing() { // Given XCTAssertEqual(router.pathCount, 0) // When router.goBack() // Then XCTAssertEqual(router.pathCount, 0) // No crash } } ``` ### Soru 9: Birden çok sekmeli karmaşık navigasyon durumları nasıl yönetilir? `TabView` içeren uygulamalar her sekme için ayrı bir navigasyon durumu gerektirir. Her sekme kendi bağımsız yığınını korur. ```swift // TabBasedNavigation.swift // Navigation state for each tab @Observable class TabNavigationState { var homePath = NavigationPath() var searchPath = NavigationPath() var profilePath = NavigationPath() var selectedTab: Tab = .home enum Tab: Hashable { case home, search, profile } func resetCurrentTab() { switch selectedTab { case .home: homePath.removeLast(homePath.count) case .search: searchPath.removeLast(searchPath.count) case .profile: profilePath.removeLast(profilePath.count) } } func resetAllTabs() { homePath.removeLast(homePath.count) searchPath.removeLast(searchPath.count) profilePath.removeLast(profilePath.count) } } struct TabRootView: View { @State private var tabState = TabNavigationState() var body: some View { TabView(selection: $tabState.selectedTab) { // Home tab NavigationStack(path: $tabState.homePath) { HomeView() .navigationDestination(for: HomeRoute.self) { route in HomeRouteView(route: route) } } .tabItem { Label("Home", systemImage: "house") } .tag(TabNavigationState.Tab.home) // Search tab NavigationStack(path: $tabState.searchPath) { SearchView() .navigationDestination(for: SearchRoute.self) { route in SearchRouteView(route: route) } } .tabItem { Label("Search", systemImage: "magnifyingglass") } .tag(TabNavigationState.Tab.search) // Profile tab NavigationStack(path: $tabState.profilePath) { ProfileView() .navigationDestination(for: ProfileRoute.self) { route in ProfileRouteView(route: route) } } .tabItem { Label("Profile", systemImage: "person") } .tag(TabNavigationState.Tab.profile) } .environment(tabState) } } ``` ### Soru 10: NavigationStack ile performans sorunlarından nasıl kaçınılır? Büyük navigasyon yığınları veya karmaşık hedefler performansı olumsuz etkileyebilir. Birkaç teknik render ve bellek kullanımını optimize eder. ```swift // NavigationPerformance.swift struct OptimizedNavigationStack: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { LazyContentView() // ✅ Lazy-loaded destinations .navigationDestination(for: HeavyRoute.self) { route in // View only created on navigation HeavyDetailView(route: route) } } } } // ✅ View with lazy content loading struct LazyContentView: View { @State private var items: [Item] = [] var body: some View { // LazyVStack only creates visible views ScrollView { LazyVStack(spacing: 12) { ForEach(items) { item in NavigationLink(value: HeavyRoute.detail(item.id)) { ItemRow(item: item) } } } } .task { items = await loadItems() } } } // ✅ Detail with progressive loading struct HeavyDetailView: View { let route: HeavyRoute @State private var data: DetailData? var body: some View { Group { if let data { DetailContent(data: data) } else { ProgressView() } } .task { // Load data only when view appears data = await loadDetailData(for: route) } } } // ❌ Avoid: eager creation of heavy views struct BadNavigationStack: View { let allItems: [Item] var body: some View { NavigationStack { List(allItems) { item in // Creates all destinations immediately NavigationLink { HeavyDetailView(item: item) // ❌ Created upfront } label: { ItemRow(item: item) } } } } } ``` > **Performans en iyi uygulamaları** > > Inline hedefli `NavigationLink` yerine her zaman `navigationDestination(for:)` kullanılmalıdır. İlk kalıp hedef view'ı yalnızca navigasyon anında yükler. ## Sonuç `NavigationStack`, tam programatik kontrol sunarak SwiftUI'da navigasyon yönetimini dönüştürür. Temel navigasyondan deep link'lere ve durum kalıcılığına kadar bu kalıpları ustalıkla kullanmak, deneyimli iOS geliştiricilerini mülakatlarda öne çıkarır. ### İnceleme listesi - ✅ `NavigationView` ile `NavigationStack` arasındaki farkı anlamak - ✅ Programatik navigasyon için `NavigationPath` kullanmayı bilmek - ✅ Navigasyon için merkezi bir router uygulamak - ✅ Yığın yeniden yapılandırması ile deep link'leri ele almak - ✅ Navigasyon durumunu `Codable` ile kalıcı hale getirip geri yüklemek - ✅ Kimlik doğrulama akışlarını ana navigasyondan ayırmak - ✅ Modal'ları `NavigationStack` ile doğru biçimde birleştirmek - ✅ Navigasyon mantığı için birim testler yazmak - ✅ `TabView` ile çoklu sekme navigasyonunu yönetmek - ✅ Büyük navigasyon yığınları için performansı optimize etmek --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/ios/swiftui-navigationstack-interview-questions