iOS Navigation (Modular MVVM-C)
Opinionated navigation enforcement for SwiftUI apps using the clinic modular architecture. Focus on coordinator + route shell wiring, feature isolation, and resilient push/sheet/deep-link flows.
Non-Negotiable Constraints (iOS 26 / Swift 6.2)
@Equatable macro on every navigation view, AnyView never
@Observable everywhere, ObservableObject / @Published never
- App-target coordinators own
NavigationPath; route shells own .navigationDestination mappings
- Coordinator-owned modal state, inline
@State booleans for sheets never
- Domain layer defines coordinator protocols; concrete coordinators stay out of feature modules
Clinic Architecture Contract (iOS 26 / Swift 6.2)
All guidance in this skill assumes the clinic modular MVVM-C architecture:
- Feature modules import
Domain + DesignSystem only (never Data, never sibling features)
- App target is the convergence point and owns
DependencyContainer, concrete coordinators, and Route Shell wiring
Domain stays pure Swift and defines models plus repository, *Coordinating, ErrorRouting, and AppError contracts
Data owns SwiftData/network/sync/retry/background I/O and implements Domain protocols
- Read/write flow defaults to stale-while-revalidate reads and optimistic queued writes
- ViewModels call repository protocols directly (no default use-case/interactor layer)
When to Apply
Reference these guidelines when:
- Designing navigation hierarchies with NavigationStack or NavigationSplitView
- Choosing between push, sheet, and fullScreenCover
- Implementing hero animations, zoom transitions, or gesture-driven dismissals
- Building multi-step flows (onboarding, checkout, registration)
- Using @Observable with @Environment and @Bindable for shared navigation state
- Reviewing code for navigation anti-patterns and modular architecture compliance
- Adding deep linking, state restoration, or tab persistence
- Ensuring VoiceOver and reduce motion support for navigation
Rule Categories by Priority
| Priority |
Category |
Impact |
Prefix |
| 1 |
Navigation Architecture |
CRITICAL |
arch- |
| 2 |
Navigation Anti-Patterns |
CRITICAL |
anti- |
| 3 | Transition & Animation | HIGH | anim- |
| 4 | Modal Presentation | HIGH | modal- |
| 5 | Flow Orchestration | HIGH | flow- |
| 6 | Navigation Performance | MEDIUM-HIGH | perf- |
| 7 | Navigation Accessibility | MEDIUM | ally- |
| 8 | State & Restoration | MEDIUM | state- |
Quick Reference
1. Navigation Architecture (CRITICAL)
arch-navigation-stack - Use NavigationStack over deprecated NavigationView
arch-value-based-links - Use value-based NavigationLink over destination closures
arch-destination-registration - Register navigationDestination at stack root
arch-destination-item - Use navigationDestination(item:) for optional-based navigation (iOS 26 / Swift 6.2)
arch-route-enum - Define routes as Hashable enums
arch-split-view - Use NavigationSplitView for multi-column layouts
arch-coordinator - Extract navigation logic into Observable coordinator
arch-observable-environment - Use @Environment with @Observable and @Bindable for shared state
arch-deep-linking - Handle deep links by appending to NavigationPath
arch-navigation-path - Use NavigationPath for heterogeneous type-erased navigation
arch-equatable-views - Apply @Equatable macro to every navigation view
arch-observable-only - Use @Observable only — never ObservableObject or @Published
arch-no-anyview - Never use AnyView in navigation — use @ViewBuilder or generics
arch-coordinator-modals - Present all modals via coordinator — never inline @State
2. Navigation Anti-Patterns (CRITICAL)
anti-mixed-link-styles - Avoid mixing NavigationLink(destination:) with NavigationLink(value:)
anti-scattered-destinations - Avoid scattering navigationDestination across views
anti-shared-stack - Avoid sharing NavigationStack across tabs
anti-hidden-back-button - Avoid hiding back button without preserving swipe gesture
anti-navigation-in-init - Avoid heavy work in view initializers
anti-hamburger-menu - Avoid hamburger menu navigation
anti-programmatic-tab-switch - Avoid programmatic tab selection changes
3. Transition & Animation (HIGH)
anim-zoom-transition - Use zoom navigation transition for hero animations (iOS 18+)
anim-matched-geometry-same-view - Use matchedGeometryEffect only within same view hierarchy
anim-spring-config - Use modern spring animation syntax (iOS 26 / Swift 6.2)
anim-gesture-driven - Use interactive spring animations for gesture-driven transitions
anim-transition-source-styling - Style transition sources with shape and background
anim-reduce-motion-transitions - Respect reduce motion for all navigation animations
anim-scroll-driven - Use onScrollGeometryChange for scroll-driven transitions (iOS 18+)
4. Modal Presentation (HIGH)
modal-sheet-vs-push - Use push for drill-down, sheet for supplementary content
modal-detents - Use presentation detents for contextual sheet sizing
modal-fullscreen-cover - Use fullScreenCover only for immersive standalone experiences
modal-sheet-placement - Place .sheet on container view, not on NavigationLink
modal-interactive-dismiss - Guard unsaved changes with interactiveDismissDisabled
modal-nested-navigation - Use separate NavigationStack inside modals
5. Flow Orchestration (HIGH)
flow-tab-independence - Give each tab its own NavigationStack
flow-multi-step - Use NavigationStack with route array for multi-step flows
flow-sidebar-navigation - Use NavigationSplitView with selection binding for sidebar
flow-tab-sidebar-adaptive - Use sidebarAdaptable TabView for iPad tab-to-sidebar (iOS 18+)
flow-pop-to-root - Implement pop-to-root by clearing NavigationPath
flow-screen-independence - Keep screens independent of parent navigation context
6. Navigation Performance (MEDIUM-HIGH)
perf-lazy-destinations - Use value-based NavigationLink for lazy destination construction
perf-task-modifier - Use .task for async data loading on navigation
perf-state-object-ownership - Own @Observable state with @State, pass as plain property
perf-avoid-body-side-effects - Avoid side effects in view body
7. Navigation Accessibility (MEDIUM)
ally-rotor-headers - Mark navigation section headers for VoiceOver rotor
ally-focus-after-navigation - Manage focus after programmatic navigation events
ally-group-navigation-elements - Group related navigation elements to reduce swipe count
ally-hide-decorative-navigation - Hide decorative navigation elements from VoiceOver
ally-keyboard-focus - Use @FocusState for keyboard navigation in forms
8. State & Restoration (MEDIUM)
state-codable-routes - Make route enums Codable for navigation persistence
state-scene-storage - Use SceneStorage for per-scene navigation persistence
state-tab-persistence - Persist selected tab with SceneStorage
state-deep-link-urls - Parse deep link URLs into route enums
state-avoid-app-level-path - Avoid defining NavigationPath at App level
How to Use
Read individual reference files for detailed explanations and code examples:
- Section definitions - Category structure and impact levels
- Rule template - Template for adding new rules
Reference Files
| File |
Description |
| references/_sections.md |
Category definitions and ordering |
| assets/templates/_template.md |
Template for new rules |
| metadata.json |
Version and reference information |
1---2name: ios-navigation-23description: iOS Navigation (Modular MVVM-C)4---5# iOS Navigation (Modular MVVM-C)67Opinionated navigation enforcement for SwiftUI apps using the clinic modular architecture. Focus on coordinator + route shell wiring, feature isolation, and resilient push/sheet/deep-link flows.89## Non-Negotiable Constraints (iOS 26 / Swift 6.2)1011- `@Equatable` macro on every navigation view, `AnyView` never12- `@Observable` everywhere, `ObservableObject` / `@Published` never13- App-target coordinators own `NavigationPath`; route shells own `.navigationDestination` mappings14- Coordinator-owned modal state, inline `@State` booleans for sheets never15- Domain layer defines coordinator protocols; concrete coordinators stay out of feature modules161718## Clinic Architecture Contract (iOS 26 / Swift 6.2)1920All guidance in this skill assumes the clinic modular MVVM-C architecture:2122- Feature modules import `Domain` + `DesignSystem` only (never `Data`, never sibling features)23- App target is the convergence point and owns `DependencyContainer`, concrete coordinators, and Route Shell wiring24- `Domain` stays pure Swift and defines models plus repository, `*Coordinating`, `ErrorRouting`, and `AppError` contracts25- `Data` owns SwiftData/network/sync/retry/background I/O and implements Domain protocols26- Read/write flow defaults to stale-while-revalidate reads and optimistic queued writes27- ViewModels call repository protocols directly (no default use-case/interactor layer)2829## When to Apply3031Reference these guidelines when:32- Designing navigation hierarchies with NavigationStack or NavigationSplitView33- Choosing between push, sheet, and fullScreenCover34- Implementing hero animations, zoom transitions, or gesture-driven dismissals35- Building multi-step flows (onboarding, checkout, registration)36- Using @Observable with @Environment and @Bindable for shared navigation state37- Reviewing code for navigation anti-patterns and modular architecture compliance38- Adding deep linking, state restoration, or tab persistence39- Ensuring VoiceOver and reduce motion support for navigation4041## Rule Categories by Priority4243| Priority | Category | Impact | Prefix |44|----------|----------|--------|--------|45| 1 | Navigation Architecture | CRITICAL | `arch-` |46| 2 | Navigation Anti-Patterns | CRITICAL | `anti-` |4748| 3 | Transition & Animation | HIGH | `anim-` |49| 4 | Modal Presentation | HIGH | `modal-` |50| 5 | Flow Orchestration | HIGH | `flow-` |51| 6 | Navigation Performance | MEDIUM-HIGH | `perf-` |52| 7 | Navigation Accessibility | MEDIUM | `ally-` |53| 8 | State & Restoration | MEDIUM | `state-` |5455## Quick Reference5657### 1. Navigation Architecture (CRITICAL)5859- [`arch-navigation-stack`](references/arch-navigation-stack.md) - Use NavigationStack over deprecated NavigationView60- [`arch-value-based-links`](references/arch-value-based-links.md) - Use value-based NavigationLink over destination closures61- [`arch-destination-registration`](references/arch-destination-registration.md) - Register navigationDestination at stack root62- [`arch-destination-item`](references/arch-destination-item.md) - Use navigationDestination(item:) for optional-based navigation (iOS 26 / Swift 6.2)63- [`arch-route-enum`](references/arch-route-enum.md) - Define routes as Hashable enums64- [`arch-split-view`](references/arch-split-view.md) - Use NavigationSplitView for multi-column layouts65- [`arch-coordinator`](references/arch-coordinator.md) - Extract navigation logic into Observable coordinator66- [`arch-observable-environment`](references/arch-observable-environment.md) - Use @Environment with @Observable and @Bindable for shared state67- [`arch-deep-linking`](references/arch-deep-linking.md) - Handle deep links by appending to NavigationPath68- [`arch-navigation-path`](references/arch-navigation-path.md) - Use NavigationPath for heterogeneous type-erased navigation69- [`arch-equatable-views`](references/arch-equatable-views.md) - Apply @Equatable macro to every navigation view70- [`arch-observable-only`](references/arch-observable-only.md) - Use @Observable only — never ObservableObject or @Published71- [`arch-no-anyview`](references/arch-no-anyview.md) - Never use AnyView in navigation — use @ViewBuilder or generics72- [`arch-coordinator-modals`](references/arch-coordinator-modals.md) - Present all modals via coordinator — never inline @State7374### 2. Navigation Anti-Patterns (CRITICAL)7576- [`anti-mixed-link-styles`](references/anti-mixed-link-styles.md) - Avoid mixing NavigationLink(destination:) with NavigationLink(value:)77- [`anti-scattered-destinations`](references/anti-scattered-destinations.md) - Avoid scattering navigationDestination across views78- [`anti-shared-stack`](references/anti-shared-stack.md) - Avoid sharing NavigationStack across tabs79- [`anti-hidden-back-button`](references/anti-hidden-back-button.md) - Avoid hiding back button without preserving swipe gesture80- [`anti-navigation-in-init`](references/anti-navigation-in-init.md) - Avoid heavy work in view initializers81- [`anti-hamburger-menu`](references/anti-hamburger-menu.md) - Avoid hamburger menu navigation82- [`anti-programmatic-tab-switch`](references/anti-programmatic-tab-switch.md) - Avoid programmatic tab selection changes8384### 3. Transition & Animation (HIGH)8586- [`anim-zoom-transition`](references/anim-zoom-transition.md) - Use zoom navigation transition for hero animations (iOS 18+)87- [`anim-matched-geometry-same-view`](references/anim-matched-geometry-same-view.md) - Use matchedGeometryEffect only within same view hierarchy88- [`anim-spring-config`](references/anim-spring-config.md) - Use modern spring animation syntax (iOS 26 / Swift 6.2)89- [`anim-gesture-driven`](references/anim-gesture-driven.md) - Use interactive spring animations for gesture-driven transitions90- [`anim-transition-source-styling`](references/anim-transition-source-styling.md) - Style transition sources with shape and background91- [`anim-reduce-motion-transitions`](references/anim-reduce-motion-transitions.md) - Respect reduce motion for all navigation animations92- [`anim-scroll-driven`](references/anim-scroll-driven.md) - Use onScrollGeometryChange for scroll-driven transitions (iOS 18+)9394### 4. Modal Presentation (HIGH)9596- [`modal-sheet-vs-push`](references/modal-sheet-vs-push.md) - Use push for drill-down, sheet for supplementary content97- [`modal-detents`](references/modal-detents.md) - Use presentation detents for contextual sheet sizing98- [`modal-fullscreen-cover`](references/modal-fullscreen-cover.md) - Use fullScreenCover only for immersive standalone experiences99- [`modal-sheet-placement`](references/modal-sheet-placement.md) - Place .sheet on container view, not on NavigationLink100- [`modal-interactive-dismiss`](references/modal-interactive-dismiss.md) - Guard unsaved changes with interactiveDismissDisabled101- [`modal-nested-navigation`](references/modal-nested-navigation.md) - Use separate NavigationStack inside modals102103### 5. Flow Orchestration (HIGH)104105- [`flow-tab-independence`](references/flow-tab-independence.md) - Give each tab its own NavigationStack106- [`flow-multi-step`](references/flow-multi-step.md) - Use NavigationStack with route array for multi-step flows107- [`flow-sidebar-navigation`](references/flow-sidebar-navigation.md) - Use NavigationSplitView with selection binding for sidebar108- [`flow-tab-sidebar-adaptive`](references/flow-tab-sidebar-adaptive.md) - Use sidebarAdaptable TabView for iPad tab-to-sidebar (iOS 18+)109- [`flow-pop-to-root`](references/flow-pop-to-root.md) - Implement pop-to-root by clearing NavigationPath110- [`flow-screen-independence`](references/flow-screen-independence.md) - Keep screens independent of parent navigation context111112### 6. Navigation Performance (MEDIUM-HIGH)113114- [`perf-lazy-destinations`](references/perf-lazy-destinations.md) - Use value-based NavigationLink for lazy destination construction115- [`perf-task-modifier`](references/perf-task-modifier.md) - Use .task for async data loading on navigation116- [`perf-state-object-ownership`](references/perf-state-object-ownership.md) - Own @Observable state with @State, pass as plain property117- [`perf-avoid-body-side-effects`](references/perf-avoid-body-side-effects.md) - Avoid side effects in view body118119120### 7. Navigation Accessibility (MEDIUM)121122- [`ally-rotor-headers`](references/ally-rotor-headers.md) - Mark navigation section headers for VoiceOver rotor123- [`ally-focus-after-navigation`](references/ally-focus-after-navigation.md) - Manage focus after programmatic navigation events124- [`ally-group-navigation-elements`](references/ally-group-navigation-elements.md) - Group related navigation elements to reduce swipe count125- [`ally-hide-decorative-navigation`](references/ally-hide-decorative-navigation.md) - Hide decorative navigation elements from VoiceOver126- [`ally-keyboard-focus`](references/ally-keyboard-focus.md) - Use @FocusState for keyboard navigation in forms127128### 8. State & Restoration (MEDIUM)129130- [`state-codable-routes`](references/state-codable-routes.md) - Make route enums Codable for navigation persistence131- [`state-scene-storage`](references/state-scene-storage.md) - Use SceneStorage for per-scene navigation persistence132- [`state-tab-persistence`](references/state-tab-persistence.md) - Persist selected tab with SceneStorage133- [`state-deep-link-urls`](references/state-deep-link-urls.md) - Parse deep link URLs into route enums134- [`state-avoid-app-level-path`](references/state-avoid-app-level-path.md) - Avoid defining NavigationPath at App level135136## How to Use137138Read individual reference files for detailed explanations and code examples:139140- [Section definitions](references/_sections.md) - Category structure and impact levels141- [Rule template](assets/templates/_template.md) - Template for adding new rules142143## Reference Files144145| File | Description |146|------|-------------|147| [references/_sections.md](references/_sections.md) | Category definitions and ordering |148| [assets/templates/_template.md](assets/templates/_template.md) | Template for new rules |149| [metadata.json](metadata.json) | Version and reference information |