SwiftUIRouter

An experimental navigation router for SwiftUI

Usage

💡 Check out ExampleApp for more.

  1. Define your routes:
import SwiftUIRouter

extension NavigationRouteLink {
  static var login: NavigationRouteLink { "/login" }
  static var signUp: NavigationRouteLink { "/register" }
  
  static func userDetails(for user: User) -> NavigationRouteLink {
    NavigationRouteLink(path: "/users/\(user.id)", meta: [
      "user": user, /// can be used when resolving route link to a View. Check below how it's used.
    ])
  }
  
  static func userDetails(for id: Int) -> NavigationRouteLink {
    NavigationRouteLink(path: "/users/\(id)")
  }
}
  1. Implement how they should be resolved:
extension Array where Element == NavigationRoute {
  static var all: [NavigationRoute] {
    let login = NavigationRoute(path: "/login", destination: LoginPage())
    let register = NavigationRoute(path: "/register", destination: RegisterPage())
    let user = NavigationRoute(path: "/users/{id}") { route in
      /// meta "user" can passed when creating `NavigationRouteLink`. Eg. in `userDetails(for user: User)`
      /// Useful when you go to user details page and want to show user something until detailed data comes in.
      UserPage(user: route.meta("user") ?? User(id: route.int("id"), name: "Unknown"))
    }

    return [login, register, user]
  }
}
  1. Create root app:
struct ExampleApp: View {
  var body: some View {
    NavigationView {
      RouterView(router: .main, root: .login)
    }
    /// I don't know why, but setting \.router in RouterView itself was not enough. Probably NavigationView
    /// overrides environment of its children somehow. So we set \.router environment again
    .environment(\.router, .main)
    
    /// This is important because default one (DoubleColumnNavigationViewStyle) is
    /// updating NavigationLink.$isActive inconsistently causing navigation bugs.
    .navigationViewStyle(StackNavigationViewStyle())
  }
}

extension NavigationRouter {
  static var main = NavigationRouter(routes: .all)
}

Benefit:

struct LoginPage: View {
  @Environment(\.router) var router
  
  var body: some View {
    VStack {
      RouterLink(.signUp) { /// or use RouterLink("/register")
        Text("Don't have an acccount? Register here")
      }

      Button("Log in") {
        router.push(link: .userDetails(for: User(id: 1, name: "Orkhan")))
        // router.push(link: .userDetails(for: /* id */ 1)
      }
    }
    .navigationTitle("Login")
  }
}

Other works

  • LayoutSwift - Yet another Swift Autolayout DSL for iOS.
  • ChainSwift - ChainSwift 🔗 is an extension that provides chainable way of setting properties.

Installation

Swift Package Manager

Note: Instructions below are for using SwiftPM without the Xcode UI. It's the easiest to go to your Project Settings -> Swift Packages and add SwiftUIRouter from there.

To integrate using Apple's Swift Package Manager , without Xcode integration, add the following as a dependency to your Package.swift:

dependencies: [
  .package(url: "https://github.com/OrkhanAlikhanov/SwiftUIRouter.git", .upToNextMajor(from: "1.0.0"))
]

and then specify "SwiftUIRouter" as a dependency of the Target in which you wish to use SwiftUIRouter.

Manually

Just drag and drop the files in the Sources folder.

Authors

See also the list of contributors who participated in this project.

GitHub

https://github.com/OrkhanAlikhanov/SwiftUIRouter