Search code examples
iosswiftswiftuiswiftui-navigationlinkios15

@Environment(\.dismiss) bug causes popped view to load a new version of itself in iOS 15


Navigate to INCR: 3 and tap either the navigation bar back button or the dismiss button and you'll notice that same view is called again but this time it's a new version because the onAppear firstLoad = true and rand is a different value.

If you comment out @Environment(\.dismiss) var dismiss and dismiss() everything works as expected as it did in iOS 14. This issue also occurs with @Environment(\.presentationMode) var presentationMode

Not sure if this is a bug or if I'm making a silly mistake, but this issue is causing a ton of problems for my app because I have to be able to programmatically dismiss a view, so any input would be appreciated.

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    @State var isPresenting = false
    
    @State var incrInt = 0
    
    @State var firstLoad = true
    
    @State var rand = Int.random(in: 1..<500)
    
    var body: some View {
        
        Text("INCR: \(incrInt) RAND: \(rand)")
        
        Button("NAVIGATE"){
            isPresenting = true
        }
        Button("DISMISS"){
           dismiss()
        }
        
        .onAppear(perform: {
            
            if firstLoad{
                print("ON APPEAR FIRST LOAD")
                print(incrInt)
                print(rand)
                print("\n")
                firstLoad = false
            }
        })
        
        NavigationLink(destination: DetailView(incrInt: (incrInt + 1)), isActive: $isPresenting){}
        
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView{
            VStack{
                DetailView()
            }
        }
    }
}

Video Link

https://i.imgur.com/qpu7NT7.mp4

Update 1: ViewModel Source of Truth

class DetailViewModel: ObservableObject {
    
    @Published var isPresenting = false
    
    var incr: Int
    
    var rand = Int.random(in: 1..<500)
    
    init(incr: Int){
        
        self.incr = incr
        
        print("INIT FIRST LOAD")
        print(incr)
        print(rand)
        print("\n")
    }
    
}

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    @StateObject var detailViewModel: DetailViewModel
    
    var body: some View {
        
        Text("INCR: \(detailViewModel.incr) RAND: \(detailViewModel.rand)")
        
        
        Button("NAVIGATE"){
            detailViewModel.isPresenting = true
        }
        Button("DISMISS"){
            dismiss()
        }
        
        
        NavigationLink(destination: DetailView(detailViewModel: DetailViewModel(incr: (detailViewModel.incr + 1))), isActive: $detailViewModel.isPresenting){}
         
        
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView{
            VStack{
                DetailView(detailViewModel: DetailViewModel(incr: 0))
            }
        }
    }
}


Solution

  • I solved it by adding .navigationViewStyle(.stack) to the NavigationView. I thought that that was the default navigation view style on iOS, but maybe that changed in iOS 15.