| name | navigator |
| description | Creates Navigator for navigation. Use when setting up navigation, adding navigation to ViewModels, or testing navigation behavior. |
Skill: Navigator
Guide for implementing navigation using NavigationCoordinator with SwiftUI NavigationStack, Navigator pattern for decoupling, and Outgoing/Incoming Navigation for cross-feature communication.
References
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ RootContainerView │
│ @State private var coordinator = NavigationCoordinator( │
│ redirector: AppNavigationRedirect() │
│ ) │
│ │
│ NavigationStack(path: $coordinator.path) { ... } │
│ .sheet(item: $coordinator.sheetNavigation) { modal in │
│ ModalContainerView(modal:appContainer:onDismiss:) │
│ } │
│ .fullScreenCover(item: $coordinator.fullScreenCoverNavigation) { ... } │
└─────────────────────────────────────────────────────────────────────────┘
Push Navigation Flow:
1. HomeNavigator.navigateToCharacters()
2. coordinator.navigate(to: HomeOutgoingNavigation.characters)
3. AppNavigationRedirect.redirect() → CharacterIncomingNavigation.list
4. NavigationStack shows CharacterListView
Modal Navigation Flow:
1. Navigator.presentFilter()
2. coordinator.present(Navigation.filter, style: .sheet(detents: [.medium, .large]))
3. sheetNavigation is set → .sheet(item:) activates
4. ModalContainerView creates its own NavigationCoordinator + NavigationStack
5. Modal can push internally or present nested modals
Navigation Types
| Type | Description | Implementation |
|---|
| Incoming | Destinations a feature can handle | {Feature}IncomingNavigation enum |
| Outgoing | Destinations a feature wants to navigate to | {Feature}OutgoingNavigation enum |
| Redirect | Connects Outgoing → Incoming | AppNavigationRedirect in App layer |
Why? Features remain decoupled. Feature A doesn't import Feature B. The App layer connects them via redirects.
Navigator Pattern
ViewModels use Navigators instead of NavigatorContract directly. This:
- Decouples ViewModels from navigation implementation details
- Makes testing easier with focused mocks
- Provides semantic navigation methods
Key Difference:
- Internal navigation: Uses
{Feature}IncomingNavigation directly
- External navigation: Uses
{Feature}OutgoingNavigation (redirected by App layer)
Modal Navigation
| Style | Description |
|---|
.sheet(detents:) | Presents as a sheet with configurable detents (default: [.large]) |
.fullScreenCover | Presents as a full-screen cover |
present(_:style:) — sets sheetNavigation or fullScreenCoverNavigation on the coordinator
dismiss() — priority: fullScreenCover > sheet > parent onDismiss
File Structure
Libraries/Core/
├── Sources/Navigation/
│ ├── NavigationCoordinator.swift
│ ├── NavigatorContract.swift
│ ├── NavigationRedirectContract.swift
│ ├── Navigation.swift
│ ├── AnyNavigation.swift
│ ├── ModalPresentationStyle.swift
│ ├── ModalNavigation.swift
│ └── DeepLinkHandler.swift
└── Mocks/
└── NavigatorMock.swift
AppKit/Sources/
├── AppContainer.swift
└── Presentation/
├── Navigation/AppNavigationRedirect.swift
└── Views/
├── NavigationContainerView.swift
├── RootContainerView.swift
└── ModalContainerView.swift
Features/{Feature}/
├── Sources/Presentation/
│ ├── Navigation/
│ │ ├── {Feature}IncomingNavigation.swift
│ │ ├── {Feature}OutgoingNavigation.swift
│ │ └── {Feature}DeepLinkHandler.swift
│ └── {Screen}/
│ ├── Navigator/
│ │ ├── {Screen}NavigatorContract.swift
│ │ └── {Screen}Navigator.swift
│ └── Tracker/
│ ├── {Screen}TrackerContract.swift
│ ├── {Screen}Tracker.swift
│ └── {Screen}Event.swift
└── Tests/Unit/Presentation/
├── Navigation/{Feature}DeepLinkHandlerTests.swift
└── {Screen}/Navigator/{Screen}NavigatorTests.swift
Checklist
Core Setup
AppKit Configuration
Feature Implementation
Testing