I want to build an application that has different tabs and each tab has access to multiple views. Currently, this is how I handle it. I have created a singleton class that has all the information for routing in it. and since each route can have its own specific enums added to its array, I can limit access on different routes. and it gives me flexibility in performing certain actions.
final class AppData: ObservableObject {
static var shared = AppData()
// Active Tab
@Published var activeTab: Tab = .home
// Map Tab
@Published var mapNavStack: [MapStack] = []
// Leaderboard Tab
@Published var leaderboardNavStack: [LeaderboardStack] = []
// Home Tab
@Published var homeNavStack: [HomeStack] = []
@Published var homeActiveTab: HomeTab = .forYou
// My Profile Tab
@Published var myProfileNavStack: [MyProfileStack] = []
@Published var myProfileActiveTab: MyProfileActiveTab = .stats
@Published var showEditProfile: Bool = false
// Authentication Tab (Only before sign-in)
@Published var authNavStack: [AuthStack] = []
func reset() {
self.activeTab = .home
self.homeNavStack.removeAll()
self.mapNavStack.removeAll()
self.leaderboardNavStack.removeAll()
self.authNavStack.removeAll()
self.myProfileNavStack.removeAll()
self.myProfileActiveTab = .stats
self.showEditProfile = false
}
}
For example, LeaderboardStack enum looks like this:
enum LeaderboardStack: Hashable {
case userProfile(id: String)
case userConnections(userId: String, initTab: UserConnectionsTab)
func hash(into hasher: inout Hasher) {
switch self {
case .userProfile(let id):
hasher.combine("userProfile")
hasher.combine(id)
case .userConnections(let userId, let tab):
hasher.combine("userConnections")
hasher.combine(userId)
hasher.combine(tab)
}
}
}
So leaderboard screen can only navigate to userProfile and userConnections.
And finally to make this all work I’m using this view modifier on LeaderboardView (Where I have Leaderboards’ NavigationStack
)
.navigationDestination(for: LeaderboardStack.self) { link in
switch link {
case .userProfile(let id):
UserProfileView(id: id)
case .userConnections(let userId, let initTab):
UserConnectionsView(userId: userId, activeTab: initTab)
}
}
But the problem is if there is a common screen that every tab can access I need to duplicate the code. But the bigger problem is when I want to navigate using NavigationLink(value:label:)
, Since I don’t know which tab it’s going to be in I can’t use it. for example, I can’t hardcode LeaderboardStack.userProfile(id: "THEID")
because it might be in the Home Tab so I’m being forced to append directly to the stack arrays based on the activeTab value.
I’m very new to Swift and SwiftUI. any help is much appreciated.
How do real-world applications handle complex routings?