Path based routing in SwiftUI

Overview

SwiftUI Router

Easy and maintainable app navigation with path based routing for SwiftUI.

SwiftUI SwiftUI Swift Xcode MIT

With SwiftUI Router you can power your SwiftUI app with path based routing. By utilizing a path based system, navigation in your app becomes more flexible and easier to maintain.

Index

Installation 🛠

In Xcode add the dependency to your project via File > Swift Packages > Add Package Dependency... and use the following url:

https://github.com/frzi/SwiftUIRouter.git

Once added, import the package in your code:

import SwiftUIRouter

Bada bing bada boom you're ready to go.


Documentation 📚


Usage 🚀

Below a quick rundown of the available views and objects and their basic features. For further details, please check out the documentation in the Swift files.

Router

Router {
	RootView()
}

The entry of a routing environment. Wrap your entire app (or just the part that needs routing) inside a Router. This view will initialize all necessary environment values needed for routes.


Route

Route(path: "news/*") {
	NewsScreen()
}
Route(path: "settings") {
	SettingsScreen()
}
Route(path: "user/:id?") { info in
	UserScreen(id: info.parameters["id"])
}

A view that will only render its contents if its path matches that of the environment. Use /* to also match deeper paths. E.g.: the path news/* will match the following environment paths: /news, /news/latest, /news/article/1 etc.

Parameters

Paths can contain parameters (aka placeholders) that can be read individually. A parameter's name is prefixed with a colon (:). Additionally, a parameter can be considered optional by suffixing it with a question mark (?). The parameters are passed down as a [String : String] in an RouteInformation object to a Route's contents.
Note: Parameters may only exist of alphanumeric characters (A-Z, a-z and 0-9) and must start with a letter.

Parameter validation

func validateUserID(routeInfo: RouteInformation) -> UUID? {
	UUID(routeInfo.parameters["id"] ?? "")
}

Route(path: "user/:id", validator: validateUserID) { userID in
	UserScreen(userID: userID)
}

A Route provides an extra step for validating parameters in a path.

Let's say your Route has the path /user/:id. By default, the :id parameter can be anything. But in this case you only want valid UUIDs. Using a Route's validator argument, you're given a chance to validate (and transform) the parameter's value.

A validator is a simple function that's passed a RouteInformation object (containing the parameters) and returns the transformed value as an optional. The new transformed value is passed down to your view instead of the default RouteInformation object. If the transformed value is nil the Route will prevent rendering its contents.


NavLink

NavLink(to: "/news/latest") {
	Text("Latest news")
}

A wrapper around a Button that will navigate to the given path if pressed.


SwitchRoutes

SwitchRoutes {
	Route(path: "latest") {
		LatestNewsScreen()
	}
	Route(path: "article/:id") { info in
		NewsArticleScreen(articleID: info.parameters["id"]!)
	}
	Route(path: ":unknown") {
		ErrorScreen()
	}
	Route {
		NewsScreen()
	}
}

A view that will only render the first Route whose path matches the environment path. This is useful if you wish to work with fallbacks. This view can give a slight performance boost as it prevents Routes from path matching once a previous Route's path is already resolved.


Navigate

Navigate(to: "/error-404")

This view will automatically navigate to another path once rendered. One may consider using this view in a fallback Route inside a SwitchRoutes.


Navigator

@EnvironmentObject var navigator: Navigator

An environment object containg the data of the Router. With this object you can programmatically navigate to another path, go back in the history stack or go forward.


RouteInformation

@EnvironmentObject var routeInformation: RouteInformation

A lightweight object containing information of the current Route. A RouteInformation contains the current path and a [String : String] with all the parsed parameters.

This object is passed down by default in a Route to its contents. It's also accessible as an environment object.


License 📄

MIT License.

Comments
  • made Navigator Equatable

    made Navigator Equatable

    I use SwiftUIRouter with Composable architecture to make things easier I've added Equatable conformance to Navigator so it can be used inTCA action or state.

    • made Navigator Equatable
    • added Tests
    opened by 3a4oT 6
  • Full screen navigation above tabView

    Full screen navigation above tabView

    How can I Create one more navigation stack above tabView?

    So I need to present a few different flows under each of tab. But I can't fine approach to do that. I had a look in examples and there is no scenario I need.

    I'll explain using one of examples: From one of the tabs I what to navigate further: Screenshot 2022-11-07 at 18 52 43

    How can I push View above tabView, not inside? image

    Please help)

    opened by IlyasNN 4
  • Type safe routes

    Type safe routes

    Currently, I'm doing this:

    enum AppRoutes {
        static let splash = "/"
        static let login = "/login"
        static let feed = "/feed"
    }
    
    SwitchRoutes {
        Route(AppRoutes.splash) {
            SplashScreenView()
        }
        Route(AppRoutes.feed) {
            FeedView()
        }
        ...
    }
    

    Is there any way to make this properly type safe, or at least safer? It seems rather fragile to rely on strings in this manner. Ideally, I wouldn't want to define my own "route enum" and have navigator.navigate accept only that type, etc.

    opened by denizdogan 4
  • Is there any way to animate transitions?

    Is there any way to animate transitions?

    Hi, first of all, congrats about this library, it is very useful.

    I wonder whether there is a way to animate switch route changes like the old-styled UIKit NavigationView actually does. Maybe there is a easy way to do it, but I didn't find it.

    Thanks in advance

    opened by joaquinperezlopez 4
  • Switch between flows

    Switch between flows

    Please provide information or example how to switch between different flows in case when one of the flow contains TabView.

    So I have registration flow and main flow with tab bar (I use original swiftUI TabView with TabContents)

    And I can't find approach how I can navigate to tabView after registration flow is completed. I get empty TabView. I suppose that's because tabView is a set of routes(views inside) and if I use tabView as root view it's okey. But if I use some empty root view as router that navigates to registration or tabView flow it can't navigate correctly to tabView

    opened by IlyasNN 3
  • Routes with missing optional parameters do not work without a trailing slash (/)

    Routes with missing optional parameters do not work without a trailing slash (/)

    Hey! Great work on this library. Really hope it takes off more because it really is a great solution for routing in SwiftUI.

    A minor bug I noticed is that while wildcards do not rely on trailing slashes, optional parameters do. So for example, I had

    Route("/chats/*")

    which would render regardless of whether I was going to /chats or /chats/:chatID. However, if I redefine this as /chats/:chatID?, then navigating to /chats produces an empty screen. Only if I navigate to /chats/ will the SwiftUI component appear.

    It's a minor regex fix I imagine, but nevertheless thought I'd raise it. I can help open a fix if you don't have the time :)

    opened by Nickersoft 3
  • Are we really limited to 10 potential routes per view?

    Are we really limited to 10 potential routes per view?

    Thanks for building this prototype! I learned quite a bit digging through it.

    I've been trying to figure out a solution for routing in a fairly large app. I was hoping this type of approach might work, but after digging in it looks to be too limited.

    Am I correct in my assumption that I could implement multiple Routers in a project (for each tab, for instance) but that the max amount of Routes per base view would be 10? So if I had a view that had 11 potential destination views, this would not work?

    Hoping I'm missing some obvious workaround. Appreciate any of your input. Thanks again!

    opened by jrames 3
  • Can't revisit a page

    Can't revisit a page

    A much needed package, thank you for bringing this to the community!

    Sadly, I ran into an issue on my first run testing things out. It's probably not the most common series of events but here's my setup:

    Router {
      SwitchRoutes {
        Route("start") {
          StartView()
        }
        Route {
          NavLink(to: "start") {
            Text("Go to start")
          }
        }
      }
    }
    
    1. When app opens, it goes to the catch-all route (as expected).

    2. Pressing NavLink takes me to StartView (as expected).

    3. StartView uses Navigator EO and a button with one line: navigate(to: "overview"). Tapping that button opens the catch-all route (as expected).

    4. When tapping NavLink again I expected it to go to the StartView again. Sometimes it navigates to a blank page and sometimes nothing occurs at all.

    Maybe this is the outside the intended functionality, if so I wonder if the documentation could better reflect that.

    EDIT: Initially the only result on Step 4 was navigating to a blank screen, so I thought this might be due to how I'm handling my onAppear/onDisappear in StartView. But now it's just staying out and not navigating at all.

    I even tried a slightly different example...

    Router {
      SwitchRoutes {
        Route("start") {
          StartView()
        }
        Route("overview") {
          NavLink(to: "info") {
            Text("Go to info")
          }
        }
        Route {
          NavLink(to: "start") {
            Text("Go to start")
          }
        }
      }
    }
    

    Now it gets to the catch all from "overview", but again nothing happens when tapping NavLink to go to "start".

    opened by zpg6 2
  • Flexible nested navigation

    Flexible nested navigation

    Imagine the following navigation pattern:

    /screen1/:id1 /screen1/:id1/:childId1 /screen1/:id1/:childId1/subchildId1 ...

    And I want all these paths to be routed to a single screen (it's like going down a filesystem). I've tried doing screen1/* - this catches all these navigations but doesn't give me parameters screen1/*/:id - doesn't work :id - doesn't work for nested navigations either

    Hence I'm taking a temporary solution of forking the library and making RouteInformation.matchedPath public to analyze it in code (which is not the best way to solve this, of course).

    What do you think would be the proper way to support this (fairly common I guess) scenario? Thanks!

    opened by mbarashkov 2
  • How to navigate to any page

    How to navigate to any page

    Hello, I am new to SwiftUI, I have a page A with a Button, when I press the Button, I want navigate to PageB, I don't know how to write the Route, can you help me, thanks.

    opened by liziyang0625 2
  • Nested routes without parent being rerendered on child navigation

    Nested routes without parent being rerendered on child navigation

    Hi and thanks for a very nice library!

    I am wondering if it would be possible to navigate between two and three without one being rerendered?

    The way we use this is that the view model of one handles data fetching and is then passed to two (and three) via .environemntObject(). But when navigating between those one is recreated on all navigation steps as well.

    @main
    struct MyApp: App {	
    	var body: some Scene {
    		WindowGroup {
    			Router(initialPath: "/one/two/three") {
    				Route(path: "/one/*") {
    					Text("One")
    
    					Route(path: "/one/two/*") {
    						Text("Two")
    
    						Route(path: "/one/two/three") {
    							Text("Three")
    						}
    					}
    				}
    			}
    		}
    	}
    }
    
    opened by blommegard 2
  • xcodebuild error

    xcodebuild error

    Have SwifUIRouter in packages. Error during build on CI: xcodebuild[70128:54646963] [MT] IDEFileReferenceDebug: [Load] <IDESwiftPackageCore.IDESwiftPackageSpecialFolderFileReference, 0x6000007fbb00: name:SwiftUIRouter.docc path:group:SwiftUIRouter.docc> Failed to load container at path: /Users/gitlab-runner/Library/Developer/Xcode/DerivedData/iosApp-asfxiubzkohtrkgwfpdkjwugwcjj/SourcePackages/checkouts/SwiftUIRouter/Sources/SwiftUIRouter.docc, Error: Error Domain=com.apple.dt.IDEContainerErrorDomain Code=6 "Cannot open "SwiftUIRouter.docc" as a "Swift Package Folder" because it is already open as a "Folder"." UserInfo={NSLocalizedDescription=Cannot open "SwiftUIRouter.docc" as a "Swift Package Folder" because it is already open as a "Folder".}

    Have spent a lot of time to fix it. Possibly this is Xcode bug. https://github.com/kean/Nuke/issues/609#issuecomment-1287798474 Guys who faced with similar issue just removed .docc folder from project while it is not fixed by Apple. Can we do the same for SwiftUIRouter? have no ideas at all how to fix it

    opened by IlyasNN 1
  • Keep paths loaded in memory for performance (e.g. in tab bar)

    Keep paths loaded in memory for performance (e.g. in tab bar)

    Is there a way to keep some paths loaded in memory, i.e. not recreate the views when the path changes?

    I was implementing a tab bar layout like in the RandomUsers example, but with this solution the pages get recreated every time the user switches tab, which can make the process of switching tab a bit laggy.

    Is there a way to keep those pages in memory to provide a smooth tab bar experience?

    Note: this would also allow keeping states, like scrolling positions. It just feels more natural.

    opened by jeanbaptistebeau 2
  • `.opacity` transition not working

    `.opacity` transition not working

    I followed the doc Animating Routes and tried to replace the transition with .opacity, but it doesn't work. When using .move(edge: .bottom).combined(with: .opacity), only the move transition is executed.

    opened by jeanbaptistebeau 2
  • Able to switch to another tab then push to a certain view under navigation view then finally present a modal?

    Able to switch to another tab then push to a certain view under navigation view then finally present a modal?

    My requirement is: Switch to another tab then push to a certain view under navigation view then finally present a modal?

    How can I config routing for this case?

    Thanks

    opened by liangwangcm 1
  • State of views from the back stack is not saved

    State of views from the back stack is not saved

    I expect back stack to work same as in system navigation: view states from the back stack should be saved.

    It includes saving @State and @StateObject annotated objects, but I can think of workaround how I can store them manually.

    The main think is scroll view position - in SwiftUI it's impossible to scroll to contentOffset, if we're using basic ScrollView, so I can't even store scroll position and restore it when view appears, unless I write my own scroll view.

    Here's a basic example to reproduce:

    1. Run the app
    2. Scroll the list
    3. Click Next button to navigate
    4. Click Back button to navigate back
    5. Scroll position is reset.
    @EnvironmentObject private var navigator: Navigator
    
    var body: some View {
    	SwitchRoutes {
    		Route("/settings") {
    			Button {
    				navigator.goBack()
    			} label: {
    				Text("back")
    			}
    		}
    		Route {
    			Text(navigator.path)
    			Button {
    				navigator.navigate("/settings")
    			} label: {
    				Text("next")
    			}
    			ScrollView {
    				ForEach(0..<100, id: \.self) { i in
    					Text("\(i)")
    				}
    			}
    		}
    	}
    }
    

    A possible workaround would be having all the screens from the back stack in the view tree - e.g. in ZStack, one on top of an other one. But this for sure would break onAppear/onDisappear, as well as transitions.

    opened by PhilipDukhov 0
  • Set Route View `id` as `self.path`

    Set Route View `id` as `self.path`

    I'm using SwiftUIRouter library on company Product, and encounter this issue frequently. https://github.com/frzi/SwiftUIRouter/issues/39

    I think that it is better to designate the Rotue's View id as self.path directly in the library than to individually.

    opened by hxperl 3
Releases(1.3.2)
  • 1.3.2(Apr 12, 2022)

  • 1.3.1(Dec 31, 2021)

  • 1.3.0(Dec 5, 2021)

    • Deprecated the path label in Route. Routes can now be initialized as Route("path/*") instead of Route(path: "path/*").
    • Added support for DocC. Documentation can now be built and viewed in Xcode.
    • Improved code documentation in comments.
    • Added links to examples in the readme.
    Source code(tar.gz)
    Source code(zip)
  • 1.2.2(Nov 27, 2021)

    • Fixed out-of-bounds crash when navigating with replace on true.
    • Fixed forward stack not updating accordingly with goBack().
    • Added additional tests for goBack() and goForward().
    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Sep 28, 2021)

  • 1.2.0(Sep 14, 2021)

    • Made Navigator's init public.
    • To go with it, Router now has an initializer allowing you to pass a manually initialize Navigator. This Navigator will be passed down as an Environment Object as usual.
    • Added a convenience initializer to Route with @autoclosure.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Aug 7, 2021)

    • Made Navigator and NavigationAction Equatable.
    • Added checks and error(s) for invalid path parameters. Parameters may only exist of alphanumeric characters (A-Z, a-z and 0-9) and must start with a letter.
    • Updated README and code documentation regarding the limited characterset of parameter names.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Aug 1, 2021)

  • 1.0.1(Jul 18, 2021)

  • 1.0.0(Apr 25, 2021)

    Almost completely rewritten from scratch.

    Changes from the top of my head:

    • Switch renamed to SwitchRoutes, to prevent conflict with SwiftUI's Switch.
    • Link renamed to NavLink, to prevent conflict with SwiftUI's Link.
    • Redirect renamed to Navigate.
    • Renamed the HistoryData environment object to Navigator. A better descriptive name.
    • Use an actual dictionary ([String : String]) to hold parameter values instead of the @ dynamicMemberLookup object in RouteInformation.
    • All paths are now relative.
    • Added support for an extra step of parameter validation in Route.
    • Detailed documentation in the source code.
    • Added tests.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Nov 26, 2019)

  • 0.1(Oct 12, 2019)

Owner
Freek
Computer graphics enthusiast. Fan of Swift and Typescript, admirer of Rust.
Freek
custom navigationBar in swiftui

SwiftUI-WRNavigationBar custom navigationBar in swiftui Requirements iOS 14.0 Installation pod 'SwiftUI-WRNavigationBar', '~>1.1.1' Overview debug cus

wangrui460 29 Nov 7, 2022
sRouting - The lightweight navigation framework for SwiftUI.

sRouting The lightweight navigation framework for SwiftUI. Overview sRouting using the native navigation mechanism in SwiftUI. It's easy to handle nav

Shiro 8 Aug 15, 2022
FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator

FlowStacks allow you to manage complex SwiftUI navigation and presentation flows with a single piece of state. This makes it easy to hoist that state into a high-level coordinator view. Using this pattern, you can write isolated views that have zero knowledge of their context within the navigation flow of an app.

John Patrick Morgan 471 Jan 3, 2023
Change SwiftUI Navigation Bar Color for different View

SwiftUINavigationBarColor Change SwiftUI NavigationBar background color per screen. Usage For NavigationBarColor to work, you have to set the Navigati

Hai Feng Kao 18 Jul 15, 2022
An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind

Composable Navigator An open source library for building deep-linkable SwiftUI applications with composition, testing and ergonomics in mind Vanilla S

Bahn-X 539 Jan 8, 2023
Coordinators in SwiftUI. Simple, powerful and elegant.

Simple, powerful and elegant implementation of the Coordinator pattern in SwiftUI. Stinsen is written using 100% SwiftUI which makes it work seamlessl

Narek Mailian 618 Jan 7, 2023
🧭 SwiftUI navigation done right

?? NavigationKit NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. ?? Installation ?? Swift Package Manager Usi

Alex Nagy 152 Dec 27, 2022
Router is a library that assists with SwiftUI view transitions.

Router Router is a library that assists with SwiftUI view transitions. Installation .package(name: "Router", url: "[email protected]:1amageek/Router.git"

1amageek 69 Dec 23, 2022
NavigationViewKit is a NavigationView extension library for SwiftUI.

NavigationViewKit 中文版说明 NavigationViewKit is a NavigationView extension library for SwiftUI. For more detailed documentation and demo, please visit 用N

东坡肘子 74 Dec 28, 2022
Navigation helpers for SwiftUI applications build with ComposableArchitecture

Swift Composable Presentation ?? Description Navigation helpers for SwiftUI applications build with ComposableArchitecture. More info about the concep

Dariusz Rybicki 52 Dec 14, 2022
Make SwiftUI Navigation be easy

VNavigator VNavigator is a clean and easy-to-use navigation in SwiftUI base on UINavigationController in UIKit Installation From CocoaPods CocoaPods i

Vu Vuong 10 Dec 6, 2022
Tools for making SwiftUI navigation simpler, more ergonomic and more precise.

SwiftUI Navigation Tools for making SwiftUI navigation simpler, more ergonomic and more precise. Motivation Tools Navigation overloads Navigation view

Point-Free 1.1k Jan 1, 2023
SwiftUINavigation provides UIKit-like navigation in SwiftUI

SwiftUINavigation About SwiftUINavigation provides UIKit-like navigation in Swif

Bhimsen Padalkar 1 Mar 28, 2022
A lightweight iOS mini framework that enables programmatic navigation with SwiftUI, by using UIKit under the hood.

RouteLinkKit A lightweight iOS mini framework that enables programmatic navigation with SwiftUI. RouteLinkKit is fully compatible with native Navigati

Αθανάσιος Κεφαλάς 4 Feb 8, 2022
SwiftUINavigator: a lightweight, flexible, and super easy library which makes SwiftUI navigation a trivial task

The logo is contributed with ❤️ by Mahmoud Hussein SwiftUINavigator is a lightwe

OpenBytes 22 Dec 21, 2022
Simple iOS app to showcase navigation with coordinators in SwiftUI + MVVM.

SimpleNavigation Simple iOS app to showcase the use of the Coordinator pattern using SwiftUI and MVVM. The implementation is as easy as calling a push

Erik Lopez 7 Dec 6, 2022
Custom navigation swiftui NavigationLink NavigationView

Custom navigation swiftui Experimenting with navigation link. if you find this idea interesting you can take and expend it into a more powerful soluti

Igor 5 Dec 2, 2022
Backported SwiftUI navigation APIs introduced in WWDC22

Navigation Backport This package uses the navigation APIs available in older SwiftUI versions (such as NavigationView and NavigationLink) to recreate

John Patrick Morgan 532 Dec 29, 2022
Customizable multi platform menu bar component with the dark and light scheme support - SwiftUI

Menu bar component (SwiftUI) Features Observing menu selection changes via generic PreferenceKey The color intensity automatically adjusts depending o

Igor 3 Aug 13, 2022