I'm trying to manage state restoration in our application. I failed to get NavigationView/NavigationLink working in pre-iOS 16 SwiftUI. I'm now trying to get it working with NavigationStack/NavigationLink/navigationDestination with iOS 16. I've built a sample application that demonstrates the issue:
import SwiftUI
@main
struct PushMultipleViews7App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
enum Route: Hashable, Codable {
case view1
case view2
case view3
case view4
}
struct ContentView: View {
@State
private var navigationPath: [Route]
@Environment(\.scenePhase)
private var scenePhase
init() {
guard let data = UserDefaults
.standard
.data(
forKey: "NavigationPath"
),
let navigationPath = try? JSONDecoder()
.decode(
[Route].self,
from: data
)
else {
self._navigationPath = State(
initialValue: []
)
print("Failed to read NavigationPath")
return
}
print("Read:",navigationPath)
self._navigationPath = State(
initialValue: navigationPath
)
}
var body: some View {
VStack {
NavigationStack(
path: $navigationPath
)
{
View1()
.navigationDestination(
for: Route.self
)
{ route in
switch route {
case .view1:
View1()
case .view2:
View2()
case .view3:
View3()
case .view4:
View4()
}
}
}
}
.onChange(
of: scenePhase
)
{ scenePhase in
guard case .inactive = scenePhase
else {
return
}
guard let data = try? JSONEncoder()
.encode(
navigationPath
)
else {
print("Failed to write NavigationPath")
return
}
print("Writing:",navigationPath)
UserDefaults
.standard
.setValue(
data,
forKey: "NavigationPath"
)
}
}
}
struct View1: View {
var body: some View {
VStack {
Text("Hello View1!")
NavigationLink(
value: Route.view2,
label: {
Text("Navigate to View2")
}
)
}
.navigationTitle("View 1")
}
}
struct View2: View {
var body: some View {
VStack {
Text("Hello View2!")
NavigationLink(
value: Route.view3,
label: {
Text("Navigate to View3")
}
)
}
.navigationTitle("View 2")
}
}
struct View3: View {
var body: some View {
VStack {
Text("Hello View3!")
NavigationLink(
value: Route.view4,
label: {
Text("Navigate to View4")
}
)
}
.navigationTitle("View 3")
}
}
struct View4: View {
var body: some View {
Text("Hello, View4!")
}
}```
To replicate the issue: from Xcode 14, run the app above in your favorite iOS 16 phone simulator. Tap on the NavigationLinks to navigate to View 4. Navigate to the Home Screen (put the app into the background). Wait a few seconds for UserDefaults to catch up. "Swipe up" to kill the app. Relaunch the app. View4 should be displaying...which is the expected behavior. Tap the "View 3" button (the "back" button) to display View3. As before, navigate to the Home Screen, wait a few seconds then "swipe up" to kill the app. Relaunch the app. Instead of displaying "View3", the app should be displaying View1. Evidence can be seen in the console when writing the navigation path to UserDefaults - it wrote "[]".
That's just one variation among lots of strange behavior I've seen trying to restore a NavigationStack's navigation path.
I've filed: FB10398702
I've tried several variations of this application: using @AppStorage and @SceneStorage instead of UserDefaults. I've also tried the sample code accompanying NavigationPath in Apple's documentation (though also based on UserDefaults).
Am I doing something incorrectly or is this a bug?
Thanks,
--David