1

I have a full code example below. The problem is that when I tap "Go to Detail" in the DogsView, the detail view is not visually pushed, although the path does get added to the path array for that router. If I tap the Cats tab and come back to the Dogs tab, I will then see the detail view. Tapping back (in the nav bar) from there returns me to the Dogs root view. What am I doing wrong?

(Note - the same thing happens if I go to the cats tab and tap "Push Stats". The StatsView won't appear until I tap the Dogs tab and then tap back to the Cats tab)

Another problem is that tapping "Go to Cats Tab" has no effect. I would expect the tab to switch since the TabView selection is based on $routeProvider.tabRouter.currentTab

Any other tips on setting up a routing system to navigate within a tab and to other tabs appreciated!

enum PetTab: Hashable {
    case dogs
    case cats
}

class PetTabRouter: ObservableObject {
    @Published var currentTab: PetTab = .dogs
}


enum PetView: Hashable {
    case detailView(itemId: String)
    case statsView
}


class AppRouter: ObservableObject {
    @Published var path: [PetView] = []
    @Published var isModalPresented: Bool = false
    @Published var modalItem: PetView?
    var tabRouter: PetTabRouter

    init(tabRouter: PetTabRouter) {
        self.tabRouter = tabRouter
    }

    func navigate(to destination: PetView) {
        path.append(destination)
    }
    
    func pop() {
        if !path.isEmpty {
            path.removeLast()
        }
    }
    
    func present(_ item: PetView) {
        modalItem = item
        isModalPresented = true
    }
    
    func dismiss() {
        if isModalPresented {
            isModalPresented = false
            modalItem = nil
        }
        else {
            pop()
        }
    }

}

class RouteProvider: ObservableObject {
    @Published var tabRouter = PetTabRouter()

    lazy var dogsRouter: AppRouter = {
        return AppRouter(tabRouter: tabRouter)
    }()
    
    lazy var catsRouter: AppRouter = {
        return AppRouter(tabRouter: tabRouter)
    }()
    
}


struct ContentView: View {
    @StateObject private var routeProvider = RouteProvider()
    
    var body: some View {
        TabView(selection: $routeProvider.tabRouter.currentTab) {
            
            DogsView()
                .tabItem { Label("Dogs", systemImage: "dog") }
                .tag(PetTab.dogs)
            
            CatsView()
                .tabItem { Label("Cats", systemImage: "cat") }
                .tag(PetTab.cats)

        }
        .environmentObject(routeProvider)
    }

}

struct DogsView: View {

    @EnvironmentObject var routeProvider: RouteProvider

    var body: some View {
        NavigationStack(path: $routeProvider.dogsRouter.path) {
            VStack {
                Text("Dogs")
                Button("Go to Detail") {
                    routeProvider.dogsRouter.navigate(to: .detailView(itemId: "123"))
                }
                Button("Go to Cats Tab") {
                    routeProvider.tabRouter.currentTab = .cats
                }
                Button("Go to Cats Tab and Push Detail View") {
                    routeProvider.catsRouter.navigate(to: .detailView(itemId: "456"))
                }
            }
            .navigationDestination(for: PetView.self) { destination in
                switch destination {
                case .detailView(let itemId):
                    DetailView(itemId: itemId)
                case .statsView:
                    StatsView()
                }
            }
        }

    }
}

struct CatsView: View {
    @EnvironmentObject var routeProvider: RouteProvider

    var body: some View {
        NavigationStack(path: $routeProvider.catsRouter.path) {
            VStack {
                Text("Cats")
                Button("Push Stats") {
                    routeProvider.catsRouter.navigate(to: .statsView)
                }
            }
            .navigationDestination(for: PetView.self) { item in
                switch item {
                case .detailView(let itemId):
                    DetailView(itemId: itemId)
                case .statsView:
                    StatsView()
                }
            }
        }
    }
}



struct DetailView: View {
    let itemId: String
    @EnvironmentObject var routeProvider: RouteProvider

    var body: some View {
        VStack {
            Text("Detail View for item \(itemId)")
            
            Button("Stats View Modal") {
                //???
            }
            
            Button("Back to Home") {
                //???
            }
        }
    }
}

struct StatsView: View {
    @EnvironmentObject var routeProvider: RouteProvider

    var body: some View {
        VStack {
            Text("Stats")
            Button("Dismiss") {
                //???
            }
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.teal)
    }
}
3
  • 2
    Don’t nest observable objects Commented Dec 23, 2024 at 19:54
  • try using Observation Commented Dec 24, 2024 at 1:35
  • Try removing the object, SwiftUI uses state and binding instead of objects. Commented Dec 24, 2024 at 10:41

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.