Contents

modern-swift-dev/pathways-swift

Pathways is a small, dependency-free Swift package for defining typed deep-link paths, converting those paths to and from Codable values, and dispatching incoming URLs to main-actor handlers.

Features

  • Strongly typed URL path parameters through Swift Codable
  • Encoding models into percent-escaped URL paths
  • Decoding URL paths into application-defined models
  • Typed and path-only routing handlers
  • Optional exact-host filtering
  • Query parameter delivery to handlers
  • Experimental fragment-route and fragment-parameter support
  • Swift 6 concurrency annotations and main-actor handler execution
  • No third-party runtime dependencies

Requirements

  • Swift 6.0 or newer
  • macOS 15 or newer
  • iOS 18 or newer
  • tvOS 18 or newer
  • watchOS 10 or newer
  • visionOS 1 or newer

Installation

In Xcode, use File > Add Package Dependencies and enter:

https://github.com/modern-swift-dev/pathways-swift.git

Or add the package in Package.swift:

dependencies: [
    .package(
        url: "https://github.com/modern-swift-dev/pathways-swift.git",
        from: "1.0.0"
    )
]

Add Pathways to the target that uses it:

.target(
    name: "YourTarget",
    dependencies: [
        .product(name: "Pathways", package: "pathways-swift")
    ]
)

Quick start

1. Define a route

Adopt Pathway and declare a path pattern. Placeholder names begin with : and correspond to Codable keys.

import Pathways

struct ProductRoute: Pathway {
    static let pattern = "/products/:productID"

    let productID: Int
}

2. Encode and decode

import Foundation
import Pathways

let path = try PathwayEncoder.shared.encode(ProductRoute(productID: 42))
// /products/42

let url = URL(string: "https://example.com/products/42")!
let route = try PathwayDecoder.shared.decode(ProductRoute.self, from: url)
// route.productID == 42

3. Register a handler

var router = Pathways()

router.register(host: "example.com", ProductRoute.self) { route, query in
    navigateToProduct(id: route.productID, campaign: query["campaign"])
}

let handled = try router.handle(url)

The handler executes on the main actor. handle(_:) returns false when no route matches and throws when a matched typed route cannot be decoded.

Route patterns

Patterns contain literal path components and named placeholders:

/teams/:teamID/members/:memberID

The model stays flat and each placeholder maps to a Codable key:

struct MemberRoute: Pathway {
    static let pattern = "/teams/:team_id/members/:member_id"

    let teamID: Int
    let memberID: UUID

    enum CodingKeys: String, CodingKey {
        case teamID = "team_id"
        case memberID = "member_id"
    }
}

Supported scalar values include strings, booleans, integer and floating-point types, raw-value Codable enums, UUIDs, and ISO 8601 dates. Nested values and collections are not supported.

Path-only routing

Register a path directly when no typed path values are needed:

router.register(host: "example.com", path: "/settings") { query in
    navigateToSettings(tab: query["tab"])
}

Path-only registrations use prefix matching by default, so /settings also matches /settings/privacy. Pass matching: .exact when the complete normalized path must match:

router.register(host: "example.com", path: "/settings", matching: .exact) { query in
    navigateToSettings(tab: query["tab"])
}

Exact matching also applies to typed routes. Each : placeholder matches one non-empty path component.

Query and fragment parameters

Handler dictionaries contain standard query parameters by default. For URLs that encode a route and parameters in the fragment, opt in per registration:

router.register(
    host: "example.com",
    path: "/callback/#complete",
    supportFragmentParams: true
) { parameters in
    completeSignIn(token: parameters["token"])
}

This supports a URL such as https://example.com/callback#complete?token=abc. Fragment handling is experimental.

Examples

The Examples directory contains standalone packages:

Each can be run independently with swift run from its directory.

Documentation

Contributing

Guides and examples live in Documentation/Site. The central documentation repository owns the shared Astro theme, builds the guides and DocC API reference, and publishes them daily. For local builds and previews, follow the docs README.

See CONTRIBUTING.md for development and maintenance instructions.

License

Pathways is available under the MIT License. See LICENSE.

Package Metadata

Repository: modern-swift-dev/pathways-swift

Default branch: main

README: README.md