
Swiftui Navigation
Centralize SwiftUI deep links, custom schemes, and Universal Links through one router so agents implement navigation consistently.
Overview
Swiftui-navigation is an agent skill for the Build phase that patterns deep links and in-app routing through a centralized SwiftUI router.
Install
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill swiftui-navigationWhat is this skill?
- Central RouterPath with handle(url:) and handleDeepLink(url:) entry points
- Inject OpenURLAction and onOpenURL at the root view with external fallback
- Patterns for Universal Links, custom URL schemes, and NSUserActivity Handoff
- Documented pitfalls section for common routing mistakes
Adoption & trust: 1.8k installs on skills.sh; 713 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
Deep links and URL schemes are handled ad hoc across views, so links fail silently or bypass system open behavior.
Who is it for?
Indie developers shipping SwiftUI apps that need Universal Links, custom schemes, or Handoff-aware routing.
Skip if: UIKit-only apps or teams that only need static tab navigation with no external URLs.
When should I use this skill?
Implementing or refactoring iOS URL routing, Universal Links, or Handoff in a SwiftUI codebase.
What do I get? / Deliverables
You get a router-centric navigation design with root-level URL hooks and clear internal-vs-external handling for shipping iOS builds.
- RouterPath-style coordinator
- Root view URL handler attachment
- Internal vs external URL decision rules
Recommended Skills
Journey fit
Routing and deep links are implemented while you are building the iOS client, not during market validation or growth analytics. Navigation, OpenURLAction, and onOpenURL belong on the SwiftUI presentation layer even when URLs originate externally.
How it compares
Opinionated SwiftUI routing patterns, not a third-party navigation framework install guide.
Common Questions / FAQ
Who is swiftui-navigation for?
Solo builders using SwiftUI who must wire deep links, federated URLs, and Handoff without spaghetti view code.
When should I use swiftui-navigation?
During Build frontend work when adding onOpenURL, OpenURLAction, Universal Links, or NSUserActivity continuation to a shipping iOS app.
Is swiftui-navigation safe to install?
It describes local routing code only; review the Security Audits panel on this Prism page for the parent swift-ios-skills package.
SKILL.md
READMESKILL.md - Swiftui Navigation
# Deep links and navigation ## Contents - [Intent](#intent) - [Core patterns](#core-patterns) - [Example: router entry points](#example-router-entry-points) - [Example: attach to a root view](#example-attach-to-a-root-view) - [Design choices to keep](#design-choices-to-keep) - [Pitfalls](#pitfalls) - [Universal Links](#universal-links) - [Custom URL Schemes](#custom-url-schemes) - [NSUserActivity Continuation (Handoff)](#nsuseractivity-continuation-handoff) ## Intent Route external URLs into in-app destinations while falling back to system handling when needed. ## Core patterns - Centralize URL handling in the router (`handle(url:)`, `handleDeepLink(url:)`). - Inject an `OpenURLAction` handler that delegates to the router. - Use `.onOpenURL` for app scheme links and convert them to web URLs if needed. - Let the router decide whether to navigate or open externally. ## Example: router entry points ```swift @MainActor final class RouterPath { var path: [Route] = [] var urlHandler: ((URL) -> OpenURLAction.Result)? func handle(url: URL) -> OpenURLAction.Result { if isInternal(url) { navigate(to: .status(id: url.lastPathComponent)) return .handled } return urlHandler?(url) ?? .systemAction } func handleDeepLink(url: URL) -> OpenURLAction.Result { // Resolve federated URLs, then navigate. navigate(to: .status(id: url.lastPathComponent)) return .handled } } ``` ## Example: attach to a root view ```swift extension View { func withLinkRouter(_ router: RouterPath) -> some View { self .environment( \.openURL, OpenURLAction { url in router.handle(url: url) } ) .onOpenURL { url in router.handleDeepLink(url: url) } } } ``` ## Design choices to keep - Keep URL parsing and decision logic inside the router. - Avoid handling deep links in multiple places; one entry point is enough. - Always provide a fallback to `@Environment(\.openURL)` via `OpenURLAction`. ## Pitfalls - Don’t assume the URL is internal; validate first. - Avoid blocking UI while resolving remote links; use `Task`. ## Universal Links Universal links let iOS open your app when a user taps a standard HTTPS URL, with no custom scheme required. They require server-side configuration and an Associated Domains entitlement. ### Apple App Site Association (AASA) Host a JSON file at `https://example.com/.well-known/apple-app-site-association` (no file extension, served with `Content-Type: application/json`): ```json { "applinks": { "details": [ { "appIDs": ["TEAMID.com.example.app"], "components": [ { "/": "/items/*", "comment": "Match item detail paths" }, { "/": "/profile/*" } ] } ] } } ``` Key rules: - AASA must be served over HTTPS with a valid certificate — no redirects. - Apple's CDN caches the file; updates can take 24-48 hours. Use `https://app-site-association.cdn-apple.com/a/v1/example.com` to verify the cached version. - Use `components` (modern) over the legacy `paths` array. ### Associated Domains entitlement In your app's `.entitlements` file (or Signing & Capabilities in Xcode), add: ```text com.apple.developer.associated-domains = [ "applinks:example.com", "applinks:www.example.com" ] ``` For development/testing, prefix with `applinks:example.com?mode=developer` to bypass the CDN cache. ### Handling Universal Links in SwiftUI Use `.onOpenURL` for link-based launches and `onContinueUserActivity` for `NSUserActivity`-based handoff: ```swift @main struct MyApp: App { @State private var router = Router() var body: some Scene { WindowGroup { ContentView() .environment(router) .onOpenURL { url in router.handle(url: url) } .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in guard let url = activity.webpageURL