Artem N0t2Day Kedrov
Artem N0t2Day Kedrov

Reputation: 21

SwiftUI NavigationStack duplicate view

Im trying to use new functionality of navigation in SwiftUI and now I've problem with similar early problem in NavigationLink when destination view created more than twice. Could you please help me to find right solution.


enum Step: String, Identifiable {
    case page1
    case page2
    case page3
    
    var id: String {
        self.rawValue
    }
}
class Router: ObservableObject {
    @Published var steps: [Step] = []
    static let shared = Router()
    private init(){}
    func showPage1() {
        steps.append(.page1)
    }
    
    func showPage2() {
        steps.append(.page2)
    }
    
    func showPage3() {
        steps.append(.page3)
    }
    
    func popToRoot() {
        steps.removeAll()
    }
}

class BaseStepViewModel: ObservableObject {
    let step: Step
    let router: Router
    init(step: Step, router: Router) {
        self.step = step
        self.router = router
        print("Created step \(step.rawValue)")
    }
    
    func shopNextPage() {
        router.showPage2()
    }
}

class Step1ViewModel: BaseStepViewModel { }

class Step2ViewModel: BaseStepViewModel {
    override func shopNextPage() {
        router.showPage3()
    }
}

class Step3ViewModel: BaseStepViewModel {
    override func shopNextPage() {
        router.popToRoot()
    }
}


struct StepView: View {
    
    @StateObject var viewModel: BaseStepViewModel
    
    var body: some View {
        Button {
            viewModel.shopNextPage()
        } label: {
            Text(viewModel.step.rawValue)
        }

    }
}

class WelcomeViewModel: ObservableObject, Identifiable {
    let title: String
    let router: Router
    var id: String {
        title
    }
    
    init(title: String, router: Router) {
        self.title = title
        self.router = router
        print("Created welcome view")
    }
    
    func startFlow() {
        router.showPage1()
    }
}

struct WelcomeView: View {
    @StateObject var viewModel: WelcomeViewModel
    
    var body: some View {
        Button {
            viewModel.startFlow()
        } label: {
            Text(viewModel.title)
        }

    }
}

struct RootView: View {
    
    @StateObject var router = Router.shared
    
    var body: some View {
        NavigationStack(path: $router.steps) {
            WelcomeView(viewModel: .init(title: "Welcome", router: router))
                    .id("Welcome")
            .navigationDestination(for: Step.self) { step in
                switch step {
                case .page1:
                    StepView(viewModel: Step1ViewModel(step: step, router: router))
                case .page2:
                    StepView(viewModel: Step2ViewModel(step: step, router: router))
                case .page3:
                    StepView(viewModel: Step3ViewModel(step: step, router: router))
                }
            }
        }
    }
}

As you can see the WelcomeViewModel created multiple times. I'm tried add a specific id to make view/viewModel unique, but it's didn't help.

Upvotes: 1

Views: 569

Answers (1)

user1046037
user1046037

Reputation: 17725

Approach:

  • When you are passing a StateObject from outside initialise it using StateObject(wrappedValue:) as shown below.

Code:

struct WelcomeView: View {
    @StateObject var viewModel: WelcomeViewModel
    
    init(viewModel: WelcomeViewModel) {
        _viewModel = StateObject(wrappedValue: viewModel)
    }
    
    var body: some View {
        Button {
            viewModel.startFlow()
        } label: {
            Text(viewModel.title)
        }
    }
}

Upvotes: 0

Related Questions