NavigationStack with NavigationPath triggers multiple init/deinit of views in stack

I’ve noticed some weird behavior when working NavigationStack paired with a NavigationPath, and I’m wondering if it’s by design or perhaps a bug.

In short:
I’m experiencing that every time I push a new view to the NavigationPath, all the previous views appear to init and deinit, which can cause all sorts of problems if you aren’t aware of it happening. It’s seems like .navigationDestination(for: ) is run once per item in the path that is given to the NavigationStack. So if you add 3 items it’ll run 3 times when adding the third view. But the original views and their state are kept.

Is this happening because pushing a view to the stack is seen as a state change? And is it intended?

The longer explanation:
So I’m developing an app in pure SwiftUI and I’m trying to establish a way of navigating through a router / coordinator. I like that my ViewModels can determine when navigation should happen and not the view. E.g. Normally I’d like to prepare some sort of data that should be transferred to the next view.

I’ve prepared an example project which you can use to check out the issue.
It’s not a full example of my setup, but it’s small enough to show what I’m experiencing. It can be found here:
https://github.com/Kimnyhuus/NavigationStackDemo

The structure is:

  • Router
  • App
    • RootView
      • AppleView + AppleViewModel
      • BananaView + BananaViewModel
      • PearView + PearViewModel

So the Router is an ObservableObject that contains a @Published NavigationPath object + functions for adding / removing to / from stack.

I’ve also added an enum here which defines the destinations that the Router can take an navigate to.

RootView is setup with a NavigationStack which is setup with the NavigationPath in the parameter:

NavigationStack(path: $router.navPath) { ... }

RootView also have the router setup as an EnvironmentObject:

.environmentObject(router)

This enables the other views to interact with the router and push new views to the stack.

Each view is initialized with its corresponding VM. The VMs contain nothing other than init and deinit, a variable containing the initialized id + a @Published num which can be set from the view. This is to keep track of the instances in the prints to the console. Each view can navigate to the two other views.

You can try and run the project yourselves, but I’ve made an example of the inits/deinits that happens here.

First, I’m navigating from RootView -> AppleView which is expected. The router prints from func navigate(to destination: Destination) that a view has been pushed to the stack. The RootView prints, when .navigationDestination(for: Destination.self) { ...} is triggered, and it says we’re navigating to .apple. And then we see that the AppleVM is inited. All like expected.

||| Router: add to navPath: 1
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 879, num: 0

Then I navigate from AppleView -> BananaView and the weird stuff starts happening. We see that a second view has been added to the stack. BananaVM is inited like we’d expect. But then the previous actions seem to run again but with new instances.

||| Router: add to navPath: 2
||| NavStack: Destination .banana
||| Init ☀️: BananaViewModel, id: 167, num: 0
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 492, num: 0

Then I navigate from BananaView -> PearView and it’s continuing.
It’s now clear that .navigationDestination(for: Destination.self) { ... } is run once per item in the stack.

||| Router: add to navPath: 3
||| NavStack: Destination .pear
||| Init ☀️: PearViewModel, id: 436, num: 0
||| NavStack: Destination .banana
||| Init ☀️: BananaViewModel, id: 292, num: 0
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 434, num: 0
||| Deinit ????: AppleViewModel, id: 492, num: 0

Finally I navigate from PearView to AppleView and it’s just piling on.

||| Router: add to navPath: 4
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 738, num: 0
||| NavStack: Destination .pear
||| Init ☀️: PearViewModel, id: 564, num: 0
||| NavStack: Destination .banana
||| Init ☀️: BananaViewModel, id: 769, num: 0
||| Deinit ????: BananaViewModel, id: 292, num: 0
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 283, num: 0
||| Deinit ????: AppleViewModel, id: 434, num: 0

Navigating back towards the RootView, you can see that it again inits and deinits different instances of the view models.
You’ll notice the original ones with state being deinit’ed where it has a number higher than 0 in “num”.

||| Router: rm navPath: 3
||| NavStack: Destination .banana
||| Init ☀️: BananaViewModel, id: 222, num: 0
||| Deinit ????: BananaViewModel, id: 769, num: 0
||| NavStack: Destination .pear
||| Init ☀️: PearViewModel, id: 801, num: 0
||| Deinit ????: PearViewModel, id: 564, num: 0
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 173, num: 0
||| Deinit ????: AppleViewModel, id: 283, num: 0
||| Deinit ????: AppleViewModel, id: 738, num: 0

||| Router: rm navPath: 2
||| NavStack: Destination .banana
||| Init ☀️: BananaViewModel, id: 26, num: 0
||| Deinit ????: BananaViewModel, id: 222, num: 0
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 744, num: 0
||| Deinit ????: AppleViewModel, id: 173, num: 0
||| Deinit ????: PearViewModel, id: 801, num: 0
||| Deinit ????: PearViewModel, id: 436, num: 3

||| Router: rm navPath: 1
||| NavStack: Destination .apple
||| Init ☀️: AppleViewModel, id: 401, num: 0
||| Deinit ????: AppleViewModel, id: 744, num: 0
||| Deinit ????: BananaViewModel, id: 26, num: 0
||| Deinit ????: BananaViewModel, id: 167, num: 2

||| Router: rm navPath: 0
||| Deinit ????: AppleViewModel, id: 401, num: 0
||| Deinit ????: AppleViewModel, id: 879, num: 1

What is making NavigationStack / .navigationDestination(for: ) run through all the previous items in the stack, every time the state changes in the NavigationPath?

I hope it’s not too confusing with all the prints 🙂
Please let me know if I need to add more info.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật