Navigation in Swift with SwiftUI

91 views Asked by At

I am a newbie in swift development with iOS. Currently I have started a project in Swift with SwiftUI that has different hierarchies of views. There are different switches between the views. I have attached a picture (architectur). My current problem is that switching between the views does not work properly for the right area (if no credentials are available) (consequently, of course, not for the left area either).

Currently I use NavigationView because I want to support iOS 15. In the @main I have placed the NavigationView and in this is the StartView as parent root. I tried to switch from the child views with the navigation link, using tag and selection via a button as the example for iOS 15 in this tutorial. Another attempt was the following code, in which currently only the right area and the mainview are implemented:

final class NavigationManager: ObservableObject {
    
    @Published var view: Views? {
        didSet {
            print("View \(view)")
        }
    }
    
    func push(to view: Views) {
        self.view = view
    }
    
    func popToRoot() {
        view = nil
    }
}

enum Views {
    case startView
    case loginView
    case mainView
    case aboutView
    case cred1View
    case cred2View
}

Unfortunately the navigation does not work as desired. Does anyone have any tips on navigation for the architecture I mentioned?

1

There are 1 answers

2
Bishow Gurung On

You can create Router like this with NavigationPath() publisher.

final class NavigationManager: ObservableObject {
    
    public enum Destination: Codable, Hashable {
        case loginView
        case mainView
    }
    
    @Published var navPath = NavigationPath()
    
    func navigate(to destination: Destination) {
        navPath.append(destination)
    }
    
    func navigateBack() {
        navPath.removeLast()
    }
    
    func navigateToRoot() {
        navPath.removeLast(navPath.count)
    }
}

For the Entry point you can init NavigationStack and bind router's path to it. and also pass router to its child views as enviroment object

@main
struct TestApp: App {
    @StateObject var router = NavigationManager()
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $router.navPath) {
                StartView()
                    .navigationDestination(for: NavigationManager.Destination.self) { destination in
                        switch destination {
                        case .loginView:
                                LoginView()
                        case .mainView:
                            MainView()
                        }
                    }
            }
            .environmentObject(router)
        }
    }
}

And finally trigger the view with isLoggedin logic like this. I'm sure you will have this logic in viewModel.

struct StartView: View {
    @EnvironmentObject var router: NavigationManager
    @State var isLoggedIn = false
    var body: some View {
        VStack {
            Image(systemName: "globe")
        }
        .padding()
        .onAppear {
            router.navigate(to: isLoggedIn ? .mainView : .loginView)
        }
    }
}