Preserve Navigation State in SwiftUI

I have a Problem preserving the Navigation state in my iOS App.
I have a NavigationSplitView (sidebar) with a list of NavigationLinks.
Each NavigationLink Navigates to a View With a NavigationStack and its own NavigationLinks with NavigationDestinations and a Navigation Path. I want to save the path using @ScenenStorage (after serialization) so when I navigate using the Sidebar, the NavigationStates of the Views are preserved. unfortunately I get the following message:

Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.
A NavigationLink is presenting a value of type “String” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.

Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.

The Navigation only works once. And only before navigation via the Sidebar.

The code looks like this:

MainView:

    var body: some View {
        NavigationSplitView(
            sidebar: { SidebarView() },
            detail: { HomeView()}
        )
    }
}

NavigationLinks in the Sidebar:

struct SidebarNavigationItem<Destination: View>: View {
    
    var option: String
    var icon: String
    var destination: Destination
    let arsColors = ARSColors.shared
    
    init(option: String, icon: String, destination: Destination) {
        self.option = option
        self.icon = icon
        self.destination = destination
    }
    
    var body: some View {
        NavigationLink(destination: destination) {
            Label {
                Text(option)
                    .foregroundColor(arsColors.primary)
            } icon: {
                Image(systemName: icon)
                    .foregroundColor(arsColors.highlight)
            }
        }
    }
}

HomeView (this is the Navigation I want to preserve)

import SwiftUI

extension [String]: RawRepresentable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([String].self, from: data)
        else {
            return nil
        }
        self = result
    }
    
    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
            let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

struct HomeView: View {
    
    @SceneStorage("navPath") var navPath: [String] = []
    
    var body: some View {
        NavigationStack(path: $navPath) {
            VStack {
                Spacer()
                
                HStack {
                    NavigationLink(value: navPath) {
                        WidgetView(widgetText: "Testnavigation")
                    }
                    
                    Spacer()
                    WidgetView(widgetText: "Wifget2")
                    Spacer()
                    WidgetView(widgetText: "Widget3")
                    Spacer()
                    WidgetView(widgetText: "Widget4")
                    Spacer()
                    WidgetView(widgetText: "Widget5")
                }
                .padding(.all)
            }
            .navigationTitle("Home")
            .navigationDestination(for: [String].self) {navPath in
                Test4NavView(navPath: navPath)
            }
        }
        .onAppear(){
            print(navPath)
        }
        .onDisappear() {
            print(navPath)
        }
    }
}

And finally the TestView


struct Test4NavView: View {
    
    var navPath: [String]
    
    @SceneStorage("input1") private var input1: String = ""
    @SceneStorage("input2") private var input2: String = ""
    @SceneStorage("input3") private var input3: String = ""
    @SceneStorage("input4") private var input4: String = ""

    
    var body: some View {
        VStack(alignment: .center) {
            
            TextField("Input 1", text: $input1)
            TextField("Input 2", text: $input2)
            TextField("Input 3", text: $input3)
            TextField("Input 4", text: $input4)
        }
        .onAppear(){
            print(navPath)
        }
        .onDisappear() {
            print(navPath)
        }
    }
}

My Expectation is to Navigate to HomeView using the Sidebar then to Test4NavView from HomeView then to a different View using the Sidebar then back to HomeView using the Sidebar and I end up on Test4NavView

Leave a Comment