iOS15 NavigationView release sequence

Hello. I have swiftUI app. Everything works fine on iOS 14. But with iOS 15 have problem.

My app have NavigationView and and i open few NavigationLink one by one. After that i remove main view and open another view. App start to deinitialize all views. In iOS 14 - this looks like this

deInit MainView.ViewModel
deInit SecondView.ViewModel
deInit ThirdView.ViewModel
deInit FourthView.ViewModel

And this is normal behavior. But in iOS 15 same code give different result.

deInit MainView.ViewModel
deInit FourthView.ViewModel
init FourthView.ViewModel
deInit ThirdView.ViewModel
deInit SecondView.ViewModel
deInit FourthView.ViewModel

sequence of release is different from ios 14 and this give problem with reinit of 4th view.

How I can fix it?

Can you show a code to reproduce the issue? Simplified but enough to reproduce.

This problem appears only if use NavigationLink(isActive: , destination: ,  label: ). If use NavigationLink(destination:, label:) - NO problems

                        

This is minimal code to reproduce.

@main
struct ios15testApp: App {
   var body: some Scene {
        WindowGroup {
            ContentView(viewModel: .init())
        }
    }
}
struct ContentView: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        
        if viewModel.showNav {
            NavigationView {
                ZStack {
                    Color.yellow
                    Button(action: {
                        viewModel.goNext = true
                    }, label: {
                            Text("go to next view")
                    })
                    NavigationLink(isActive: $viewModel.goNext,
                                   destination: {
                        View1(viewModel: .init())
                    },
                                   label: {
                        EmptyView()
                    })
                }
            }
            .navigationViewStyle(StackNavigationViewStyle())
        }
        else {
            ZStack {
                Color.red
                Text("New View")
            }
        }
    }
}
extension ContentView {
        class ViewModel: ObservableObject {
        @Published var showNav = true
        @Published var goNext = false
                var cancellable: Cancellable?
        init() {
            DeinitHelper.onInit(object: self)
            
            cancellable = NotificationCenter.default
                .publisher(for:  Notification.Name("ChangeView"))
                    .sink() { _ in
                        self.showNav.toggle()
                    }
        }
        deinit {
            DeinitHelper.onDeinit(object: self)
        }
    }
}
struct View1: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        ZStack {
            Button(action: {
                viewModel.goNext = true
            }, label: {
                Text("Go Next")
            })
            NavigationLink(isActive: $viewModel.goNext,
                           destination: {
                View2(viewModel: .init())
            },
                           label: {
                EmptyView()
            })
        }
    }
}
extension View1 {
    class ViewModel: ObservableObject {
        @Published var goNext = false
        init() {
            DeinitHelper.onInit(object: self)
        }
        deinit {
            DeinitHelper.onDeinit(object: self)
        }
    }
}

struct View2: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        ZStack {
            Button(action: {
                viewModel.goNext = true
            }, label: {
                Text("Go Next")
            })
            NavigationLink(isActive: $viewModel.goNext,
                          destination: {
                View3(viewModel: .init())
            },
                           label: {
                EmptyView()
            })
        }
    }
}
extension View2 {
   class ViewModel: ObservableObject {
        @Published var goNext = false
        init() {
            DeinitHelper.onInit(object: self)
        }
        deinit {
            DeinitHelper.onDeinit(object: self)
        }
    }
}
struct View3: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        ZStack {
            Button(action: {
                viewModel.goNext = true
            }, label: {
                Text("Go Next")
            })
            NavigationLink(isActive: $viewModel.goNext,
                           destination: {
                View4(viewModel: .init())
            },
                           label: {
                EmptyView()
            })
        }
    }
}
extension View3 {
    class ViewModel: ObservableObject {
        @Published var goNext = false
        init() {
            DeinitHelper.onInit(object: self)
        }
        deinit {
            DeinitHelper.onDeinit(object: self)
        }
    }
}
struct View4: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        Button(action: {
            NotificationCenter.default.post(name: Notification.Name("ChangeView"), object: nil)
        }) {
            Text("Change Main View")
        }
    }
}
extension View4 {
    class ViewModel: ObservableObject {
        init() {
            DeinitHelper.onInit(object: self)
        }
        deinit {
            DeinitHelper.onDeinit(object: self)
        }
    }
}
import os
struct DeinitHelper {
    static func onDeinit(object: AnyObject) {
        os_log("deInit %s", log: Log.debug, type: .debug, String(describing: object))
    }
    static func onInit(object: AnyObject) {
        os_log("!!! Init %s", log: Log.debug, type: .debug, String(describing: object))
    }
}
struct Log {
    static let debug = OSLog(subsystem: "Test", category: "debug")
}

Log from iOS 15.

2021-09-18 10:21:43.066023+0300 ios15test[43190:444462] [debug] !!! Init ios15test.ContentView.ViewModel
2021-09-18 10:21:44.220878+0300 ios15test[43190:444462] [debug] !!! Init ios15test.View1.ViewModel
2021-09-18 10:21:45.562711+0300 ios15test[43190:444462] [debug] !!! Init ios15test.View2.ViewModel
2021-09-18 10:21:46.238179+0300 ios15test[43190:444462] [debug] !!! Init ios15test.View3.ViewModel
2021-09-18 10:21:47.635900+0300 ios15test[43190:444462] [debug] !!! Init ios15test.View4.ViewModel

2021-09-18 10:21:48.873941+0300 ios15test[43190:444462] [debug] deInit ios15test.View4.ViewModel
2021-09-18 10:21:48.875682+0300 ios15test[43190:444462] [debug] deInit ios15test.View2.ViewModel
2021-09-18 10:21:48.875730+0300 ios15test[43190:444462] [debug] deInit ios15test.View1.ViewModel
2021-09-18 10:21:48.875874+0300 ios15test[43190:444462] [debug] deInit ios15test.View3.ViewModel
2021-09-18 10:21:48.876261+0300 ios15test[43190:444462] [debug] !!! Init ios15test.View4.ViewModel
2021-09-18 10:21:50.473198+0300 ios15test[43190:444462] [debug] deInit ios15test.View4.ViewModel

Log from iOS 14

2021-09-18 11:24:18.957072+0300 ios15test[44741:493113] [debug] !!! Init ios15test.ContentView.ViewModel
2021-09-18 11:24:20.372983+0300 ios15test[44741:493113] [debug] !!! Init ios15test.View1.ViewModel
2021-09-18 11:24:21.018618+0300 ios15test[44741:493113] [debug] !!! Init ios15test.View2.ViewModel
2021-09-18 11:24:22.048887+0300 ios15test[44741:493113] [debug] !!! Init ios15test.View3.ViewModel
2021-09-18 11:24:23.003798+0300 ios15test[44741:493113] [debug] !!! Init ios15test.View4.ViewModel

2021-09-18 11:24:23.619264+0300 ios15test[44741:493113] [debug] deInit ios15test.View1.ViewModel
2021-09-18 11:24:23.619511+0300 ios15test[44741:493113] [debug] deInit ios15test.View2.ViewModel
2021-09-18 11:24:23.619724+0300 ios15test[44741:493113] [debug] deInit ios15test.View3.ViewModel
2021-09-18 11:24:24.518013+0300 ios15test[44741:493113] [debug] deInit ios15test.View4.ViewModel

I tested this code on iOS 15 simulator (XCode 13ß5).

I tap on next til view 4 and get the following log (I kept the typo : Writing analzed variants), which is different from yours:

2021-09-18 12:53:46.182806+0200 SwiftUI-Main[3837:164791] [debug] !!! Init SwiftUI_Main.ContentView.ViewModel 2021-09-18 12:54:07.841585+0200 SwiftUI-Main[3837:164791] [debug] !!! Init SwiftUI_Main.View1.ViewModel 2021-09-18 12:54:10.611254+0200 SwiftUI-Main[3837:164791] [debug] !!! Init SwiftUI_Main.View2.ViewModel 2021-09-18 12:54:12.254443+0200 SwiftUI-Main[3837:164791] [debug] !!! Init SwiftUI_Main.View3.ViewModel 2021-09-18 12:54:13.311152+0200 SwiftUI-Main[3837:164791] [debug] !!! Init SwiftUI_Main.View4.ViewModel 2021-09-18 12:54:15.299835+0200 SwiftUI-Main[3837:164791] [debug] deInit SwiftUI_Main.View4.ViewModel 2021-09-18 12:54:15.305149+0200 SwiftUI-Main[3837:164791] [debug] deInit SwiftUI_Main.View3.ViewModel 2021-09-18 12:54:15.305296+0200 SwiftUI-Main[3837:164791] [debug] deInit SwiftUI_Main.View2.ViewModel 2021-09-18 12:54:15.305439+0200 SwiftUI-Main[3837:164791] [debug] deInit SwiftUI_Main.View1.ViewModel 2021-09-18 12:56:31.451555+0200 SwiftUI-Main[3837:164791] Writing analzed variants. 2021-09-18 12:56:31.454923+0200 SwiftUI-Main[3837:164791] Writing analzed variants.

But I do not the see problem it creates.

Which version of XCode do you use ?

iOS15 NavigationView release sequence
 
 
Q