Changing destination value doesn't open the screen

57 views Asked by At

I am trying to implement enum based navigation with SwiftUINavigation framework. In my main app I try to setup deeplinking but I faced a problem with navigation links. When I change destination, the app closes the initial screen but doesn't open the new screen.

It seems NavigationLink binding nullifies the value a second time when there is already a new value. When I click Open Second Screen button on the first screen I expect it to close the first screen and open the second screen. Here is a sample code:


import SwiftUI
import SwiftUINavigation

struct ContentView: View {
    enum Destination {
        case first
        case second
    }

    @State var destination: Destination?

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
                    unwrapping: $destination.case(/Destination.first),
                    onNavigate: { isActive in
                        if isActive {
                            destination = .first
                        }
                    },
                    destination: { token in
                        NextView(buttonTitle: "Open Second View") {
                            destination = .second
                        } close: {
                            destination = nil
                        }
                    },
                    label: {
                        Text("Open First View")
                    }
                )

                NavigationLink(
                    unwrapping: $destination.case(/Destination.second),
                    onNavigate: { isActive in
                        if isActive {
                            destination = .second
                        }
                    },
                    destination: { token in
                        NextView(buttonTitle: "Open First View") {
                            destination = .first
                        } close: {
                            destination = nil
                        }
                    },
                    label: {
                        Text("Open Second View")
                    }
                )
            }
        }
    }
}

struct NextView: View {
    let buttonTitle: String
    let closure: () -> Void
    let close: () -> Void

    var body: some View {
        VStack {
            Button(buttonTitle, action: closure)
            Button("Close", action: close)
        }
    }
}

I am using Xcode 14.2, Simulator iPhone 8 iOS 15.5

1

There are 1 answers

1
Benzy Neez On

So the framework SwiftUINavigation doesn't seem to be working for you. You are also using NavigationView, which is deprecated.

I would suggest you try implementing it using a NavigationStack, which works with enums natively.

Here is an example implementation. When you go from First -> Second -> First, it goes back to the previous page, instead of going deeper. This is done in the function goToTarget. You could change the implementation if you wanted it to behave differently.

import SwiftUI
//import SwiftUINavigation

struct ContentView: View {
    enum Destination {
        case first
        case second
    }

    @State private var navPath = [Destination]()

    private func goBackToMenu() {
        navPath.removeAll()
    }

    private func goToTarget(destination: Destination) {
        if navPath.contains(destination) {
            while !navPath.isEmpty && navPath.last != destination {
                navPath.removeLast()
            }
        } else {
            navPath.append(destination)
        }
    }

    private var launchFirstView: some View {
        NextView(
            buttonTitle: "Open Second View",
            closure: { goToTarget(destination: .second) },
            close: goBackToMenu
        )
    }

    private var launchSecondView: some View {
        NextView(
            buttonTitle: "Open First View",
            closure: { goToTarget(destination: .first) },
            close: goBackToMenu
        )
    }

    var body: some View {
        NavigationStack(path: $navPath) {
            VStack(spacing: 20) {
                NavigationLink(value: Destination.first) {
                    Text("Open First View")
                }

                NavigationLink(value: Destination.second) {
                    Text("Open Second View")
                }
            }
            .navigationDestination(for: Destination.self) { destination in
                switch destination {
                case .first: launchFirstView
                case .second: launchSecondView
                }
            }
        }
    }
}