Workaround for


SwiftUI does not allow programmable screen navigation out of the box, has many bugs and limited screen manipulation capabilities. This repository provides a workaround to use routing programmatically.

Steps to use

  1. Configure routes Use enum for routes and dont forget to choose screens with NavigationView!

enum ScreenRoute: ScreenProtocol {
    case start
    case navigator
    case fullScreen
    case sheetScreen
    var embedInNavView: Bool {
        switch self {
        case .start, .sheetScreen:
            return true
        case .navigator, .fullScreen:
            return false
  1. Create router factory. This class maps routes and creates views.

class ScreenRouterFactory: RouterFactory {
    @ViewBuilder func makeBody(for screen: ScreenRoute) -> some View {
        switch screen {
        case .start:
        case .navigator:
        case .fullScreen:
        case .sheetScreen:
  1. (? optional) Create typealias for easy naming Routers.
typealias ScreenRouter = Router<ScreenRoute, ScreenRouterFactory>
  1. Create Router instance. You can create in Views, ViewModels or use in DI. Don’t forget to use @StateObject or similar wrappers in views for re-render
let screenRouter = ScreenRouter(rootScreen: .start, factory: ScreenRouterFactory())
  1. Use it! Start routing with start method. In this example EnvObject used:

struct MainView: View {
    @EnvironmentObject var screenRouter: ScreenRouter
    var body: some View {
  1. Available Router’s methods:

func start() -> some View
func presentSheet(_ screen: ScreenType)
func dismissLast()
func navigateTo(_ screen: ScreenType)
func presentFullScreen(_ screen: ScreenType)
func popToRoot()


View Github