Another Approach to Screen Routing in SwiftUI

Muhammad Rasyad Caesarardhi - Jul 3 - - Dev Community

First of all, why custom routing? there are several answers and reasons for implementing custom routing in Swift. from the technical side, implementing custom routing will significantly reduce boilerplate code that tends to be nasty to implement another view from other files. the approach is similar to when we use routing from web frameworks like ExpressJS, Laravel PHP, and others. Since it was similar to web frameworks, the amount of time needed to learn the pattern could be reduced. Thus, increased productivity when working with several developers because it was much easier to implement routing for different types of views in SwiftUI. this easiness would be very beneficial with lots and lots (and different types) of screens and Views by collaborating and delegating tasks of different views within the development team.

okay, enough talk. So how would we implement a web approach to routing in SwiftUI projects? first, we need to create 2 files of type swift. one file would store our Routing data and path that was chosen by the user or we could simply be called it a Router model.

// Model/Router.swift
import SwiftUI

class Router: ObservableObject {
    @Published var path: [Destination] = []

    enum Destination: String, Hashable {
        case SplashScreen, Home, Story, SecondStory
    }

    static let shared: Router = .init()
}
Enter fullscreen mode Exit fullscreen mode

okay, let's talk about it some more, we see that SwiftUI was imported. this is because we need the ObservableObject protocol on our class for the Router model. we specifically need the @Published to make sure our path can be looked at by the Views later. we store our user routing and movement between screens/views activity within the path variable. the path variable is defined as an array of Destination enum. we use the enum Destination as a way to force the type of page that the user can visit. this enforces the code to look for the Destination in the enum first and helps bug checking even before the app is built into production. a singleton pattern is also used by using a shared variable and initializing the class itself inside its class to be shared with other files and views.

// Routes/RouteView.swift
import SwiftUI

struct RouteView: View {
    @StateObject private var navPath = Router.shared

    var body: some View {
        NavigationStack(path: $navPath.path) {
                // This is the root page view (you can use anything)
                // It does not have to be SplashScreenView
            SplashScreenView()
            // end of the root page view
                .toolbar(.hidden)
                .navigationDestination(for: Router.Destination.self) { destination in
                    switch destination {
                    case .Story:
                        StoryFirstView()
                    case .SecondStory:
                        StorySecondView()
                    case .Home:
                        HomeView()
                    case .SplashScreen:
                        SplashScreenView()
                    }
                }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Secondly, the RouteView. this is where we define the routes for our SwiftUI projects. the @StateObject variable of navPath is used to detect changes in the @Published variable in the Router model beforehand. thus it would render specifically based on the current new state of the array path in $navPath.path which is a binding. we also need to define every case of the Destination enum in the Router model or it would fail because the switch statement needs to be exhaustive of the Destination enum.

Lastly, you can call this RouteView in any place and it would have this nice web-like routing that would be beneficial for the development team.

cheers!

. .