axiom-codex/skills/axiom-audit-swiftui-nav/SKILL.md
Use when the user mentions SwiftUI navigation issues, deep linking problems, state restoration bugs, or navigation architecture review.
npx skillsauth add charleswiltgen/axiom axiom-audit-swiftui-navInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert at detecting SwiftUI navigation issues — both known anti-patterns AND missing/incomplete navigation architecture that causes deep link failures, state loss, and broken user journeys.
Run a comprehensive navigation audit using 5 phases: map the navigation architecture, detect known anti-patterns, reason about what's missing, correlate compound issues, and score navigation health. Report all issues with:
Note: This agent checks navigation architecture and correctness. For performance issues, use swiftui-performance-analyzer.
Skip: *Tests.swift, *Previews.swift, */Pods/*, */Carthage/*, */.build/*, */DerivedData/*, */scratch/*, */docs/*, */.claude/*, */.claude-plugin/*
Before grepping for issues, build a mental model of the app's navigation structure.
Glob: **/*.swift (excluding test/vendor paths)
Grep for:
- `NavigationStack` — stack-based navigation
- `NavigationSplitView` — master-detail navigation
- `TabView` — tab structure
- `UINavigationController`, `UITabBarController` — UIKit navigation
Grep for:
- `NavigationPath`, `@State.*path` — programmatic navigation state
- `.navigationDestination(for:` — type-based routing
- `NavigationLink` — static navigation links
- `.sheet`, `.fullScreenCover` — modal presentations
- `.onOpenURL` — deep link handlers
- `@SceneStorage` — state preservation
Read 2-3 key navigation files to understand:
Write a brief Navigation Architecture Map (8-12 lines) summarizing:
Present this map in the output before proceeding.
Run all 10 existing detection patterns. These are fast and reliable. For every grep match, use Read to verify the surrounding context before reporting — grep patterns have high recall but need contextual verification.
Pattern: NavigationStack without path binding
Search: NavigationStack { or NavigationStack() without path: parameter — compare against @State.*NavigationPath count
Issue: Can't navigate programmatically or handle deep links
Fix: Add @State private var path = NavigationPath() and bind with NavigationStack(path: $path)
Pattern: Missing deep link handling
Search: Check for .onOpenURL handler; check Info.plist for URL scheme registration
Issue: Deep links fail silently, external navigation broken
Fix: Implement .onOpenURL handler that routes to correct NavigationPath destination
Pattern: Missing .navigationDestination(for:) for path types
Search: .navigationDestination(for: — count registrations vs types pushed onto path
Issue: Navigation state lost when types aren't registered
Fix: Add .navigationDestination(for:) for every type used in NavigationPath
Pattern: Wrong navigation container for the use case
Search: NavigationStack in master-detail contexts (iPad apps); NavigationSplitView for linear flows
Issue: Poor iPad/Mac experience, wasted screen space
Fix: Use NavigationSplitView for master-detail, NavigationStack for linear flows
Pattern: Multiple .navigationDestination with same type
Search: Multiple .navigationDestination(for: with the same type parameter
Issue: Undefined behavior — wrong view shown, navigation breaks
Fix: Use unique types or wrapper enum with associated values
Pattern: Missing sidebar adaptable style (iOS 18+)
Search: TabView with NavigationStack but no .tabViewStyle(.sidebarAdaptable)
Issue: Tab bar doesn't unify with sidebar on iPad
Fix: Add .tabViewStyle(.sidebarAdaptable)
Pattern: No persistence for navigation path
Search: Absence of @SceneStorage for navigation path data
Issue: User loses their place when app is terminated by system
Fix: Store NavigationPath data in @SceneStorage with Codable encoding
Pattern: Using deprecated iOS 16+ APIs
Search: NavigationLink.*isActive: or NavigationLink.*tag:.*selection:
Issue: Deprecated, will be removed in future iOS versions
Fix: Migrate to NavigationStack + NavigationPath pattern
Pattern: Navigation logic scattered across views
Search: Multiple files with path.append(, navigation logic in leaf views
Issue: Hard to reason about navigation flow, difficult to add deep links
Fix: Centralize in coordinator/router
Pattern: No explicit sidebar visibility management
Search: NavigationSplitView without @State var visibility: NavigationSplitViewVisibility
Issue: Can't programmatically control sidebar visibility
Fix: Add @State var visibility: NavigationSplitViewVisibility and bind
Using the Navigation Architecture Map from Phase 1 and your domain knowledge, check for what's missing — not just what's wrong.
| Question | What it detects | Why it matters | |----------|----------------|----------------| | Are there .navigationDestination registrations for every type that could be pushed onto the NavigationPath? | Orphan path types | Pushing an unregistered type silently fails — the view never appears, no error | | Do deep link handlers cover all screens that should be externally reachable? | Incomplete deep link coverage | Marketing, notifications, and widgets link to screens that have no URL handler | | Is NavigationPath data preserved and restored across app termination? | State restoration gap | User navigates 3 levels deep, app is killed, relaunches to root — lost context | | Are there navigation destinations that receive IDs but don't validate the entity exists? | Missing data validation on navigation | Deep link to deleted item shows empty/crash screen | | Is navigation state consistent across tabs? (e.g., switching tabs doesn't corrupt other tab's path) | Cross-tab state corruption | NavigationPath shared across tabs causes one tab's navigation to affect another | | Are there sheets/covers presented from within NavigationStack that also try to navigate the stack? | Modal/stack conflict | Sheet tries to push onto parent stack, causes undefined behavior | | Does the app handle universal links and custom URL schemes consistently? | Inconsistent link handling | Universal links work but custom scheme doesn't, or vice versa |
For each finding, explain what's missing and why it matters. Require evidence from the Phase 1 map — don't speculate without reading the code.
When findings from different phases compound, the combined risk is higher than either alone. Bump the severity when you find these combinations:
| Finding A | + Finding B | = Compound | Severity | |-----------|------------|-----------|----------| | Missing NavigationPath | Deep link handler exists | Deep links received but can't navigate programmatically | CRITICAL | | Orphan .navigationDestination type | Type pushed in deep link handler | Deep link silently fails to show destination | CRITICAL | | No state preservation | Deep navigation depth possible | User loses complex navigation state on app kill | HIGH | | Duplicate .navigationDestination type | Used in different tabs | Type collision causes wrong tab's view to appear | HIGH | | Deprecated NavigationLink | In core navigation flow | Migration debt in critical path | HIGH | | Wrong container (Stack on iPad) | Deep link to detail view | Deep link shows phone-style navigation on iPad | MEDIUM | | Modal presented from NavigationStack | Modal tries to push onto stack | Modal/stack navigation conflict | HIGH |
Also note overlaps with other auditors:
Calculate and present a health score:
## Navigation Health Score
| Metric | Value |
|--------|-------|
| Path coverage | N NavigationStacks, M with NavigationPath binding (Z%) |
| Destination coverage | N types pushed, M registered with .navigationDestination (Z%) |
| Deep link coverage | N screens, M reachable via deep link (Z%) |
| State preservation | NavigationPath persisted: yes/no |
| Deprecated APIs | N deprecated NavigationLink usages |
| Container correctness | NavigationStack/SplitView used appropriately: yes/no |
| **Health** | **SOLID / FRAGILE / BROKEN** |
Scoring:
# SwiftUI Navigation Audit Results
## Navigation Architecture Map
[8-12 line summary from Phase 1]
## Summary
- CRITICAL: [N] issues
- HIGH: [N] issues
- MEDIUM: [N] issues
- LOW: [N] issues
- Phase 2 (anti-pattern detection): [N] issues
- Phase 3 (completeness reasoning): [N] issues
- Phase 4 (compound findings): [N] issues
## Navigation Health Score
[Phase 5 table]
## Issues by Severity
### [SEVERITY] [Category]: [Description]
**File**: path/to/file.swift:line
**Phase**: [2: Detection | 3: Completeness | 4: Compound]
**Issue**: What's wrong or missing
**Impact**: What users experience
**Fix**: Code example showing the fix
**Cross-Auditor Notes**: [if overlapping with another auditor]
## Recommendations
1. [Immediate actions — CRITICAL fixes (deep link gaps, type collisions)]
2. [Short-term — HIGH fixes (state preservation, missing destinations)]
3. [Long-term — architectural improvements from Phase 3 findings]
If >50 issues in one category: Show top 10, provide total count, list top 3 files If >100 total issues: Summarize by category, show only CRITICAL/HIGH details
For navigation patterns, debugging, and API reference: axiom-swiftui skill (navigation)
development
Use when building ANY watchOS app — app structure, independent apps, Watch Connectivity, Smart Stack widgets, complications, controls, RelevanceKit, background tasks, ClockKit migration.
development
Use when working with HealthKit, WorkoutKit, health data, workouts, or fitness features on iOS or watchOS. Covers permissions, queries, background delivery, custom workouts, multidevice coordination.
development
Use when building, fixing, or improving ANY SwiftUI UI — views, navigation, layout, animations, performance, architecture, gestures, debugging, iOS 26 features.
content-media
Use when working with camera, photos, audio, haptics, ShazamKit, or Now Playing. Covers AVCaptureSession, PHPicker, PhotosPicker, AVFoundation, Core Haptics, audio recognition, MediaPlayer, CarPlay, MusicKit.