Home > Net >  NavigationLink jumps back when @ObservedObject is changed and @AppStorage is used
NavigationLink jumps back when @ObservedObject is changed and @AppStorage is used

Time:02-08

I've tracked down an annoying bug in my code and managed to recreate the issue using xCode's default iOS app as a starting point and adding a few bits of code.

Problem

Modifying part of an @ObservedObject (core data entity) causes the View to immediately jump back, when the following conditions are true:

  1. The View is at least 2 links deep
  2. @AppStorage is declared in another view

I've confirmed that (1) if I skip SubView2 and link straight to SubView3 (the editing view) then the behavior does not occur and (2) if I comment out the @AppStorage line in SettingsView() then the behavior does not occur.

enter image description here

In SubView3, I tap "Increase" to increase item.value 1. This causes the view to jump back to SubView2.

Code

I started a default Xcode project with CloudKit and SwiftUI, then added a SettingsView (which contains the @AppStorage) and two SubViews to view a specific item.

import SwiftUI
import CoreData

struct SettingsView: View {
    
    @AppStorage("userSettingOne") var userSettingOne = false
    
    var body: some View {
        Text("Hello")
    }
}


struct SubView2: View {
    
    @ObservedObject var item2: Item
    
    @State private var showSubView3 = false
        
    var body: some View {
        
        NavigationLink(destination: SubView3(item3: item2) , isActive: $showSubView3) { EmptyView() }
                
        Text("Value: \(item2.value)")
        
        Button("SubView3", action: {
            showSubView3 = true
        })
            .navigationTitle("SubView2")
    }
}


struct SubView3: View {
        
    @ObservedObject var item3: Item

    var body: some View {
        
        VStack {
            Text("Value: \(item3.value)")
            Button("Increase", action: {
                item3.value  = 1
            })
        }
            .navigationTitle("SubView3")
    }
}


struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
        
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    
                    NavigationLink("Item", destination: SubView2(item2: item))
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem {
                    HStack {
                        Button(action: addItem) {
                            Label("Add Item", systemImage: "plus")
                        }
                        NavigationLink(destination: SettingsView(), label: {
                            Label("", systemImage: "gear").labelStyle(.iconOnly)
                        })
                    }

                }
            }
            Text("Select an item")
        }
    }
    
    // Boilerplate/default code below here

    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

enter image description here

CodePudding user response:

try adding .navigationViewStyle(.stack) to your NavigationView

  •  Tags:  
  • Related