EnvironmentObject not injected for .navigationDestination

Consider code

@EnvironmentObject var navModel: NavigationModel
var body: some View {
    someView
       .navigationDestination(for: ImageModel.self) { imageModel in
                ImageDetailedView(image: imageModel)
                    .environmentObject(navModel)   //this is required 
       }
}

Is navigation not considered a child of the view? And if so, is it normal to keep throwing around environemntObjects around the navigation stack?

import Combine
import SwiftUI

enum Destination {
    case firstPage
    case secondPage
}

enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
    case dessert
    case pancake
    case salad
    case sandwich
    
    var id: Int { rawValue }
    
    var localizedName: LocalizedStringKey {
        switch self {
        case .dessert:
            return "Dessert"
        case .pancake:
            return "Pancake"
        case .salad:
            return "Salad"
        case .sandwich:
            return "Sandwich"
        }
    }
}

@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
    @Published var path = NavigationPath()
    
    func gotoHomePage() {
        path.removeLast(path.count)
    }
    
    func tapOnEnter() {
        path.append(Destination.firstPage)
    }
    
    func tapOnFirstPage() {
        path.append(Destination.secondPage)
    }
    
    func tapOnSecondPage() {
        path.removeLast()
    }
    
    func test() {
        path.removeLast(path.count)
        path.append(2)
    }
}

class Test :ObservableObject {
    var name = "test"
}

@available(iOS 16.0, *)
struct SplitTestView: View {
    @State var selectedCategory: Category?
    var categories = Category.allCases
    @ObservedObject var coordinator = Coordinator3()
    @StateObject var test = Test()
    
    var body: some View {
        NavigationSplitView {
            List(categories, selection: $selectedCategory) { category in
                NavigationLink(category.localizedName, value: category)
            }
        } detail: {
            NavigationStack(path: $coordinator.path) {
                switch selectedCategory {
                case .dessert:
                    Text(selectedCategory!.localizedName)
                case .pancake:
                    VStack {
                        Text("Navigation stack")
                            .padding()
                        NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
                            .padding()
                        NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
                            .padding()
                    }
                    .navigationDestination(for: Destination.self) { destination in
                        if destination == .firstPage {
                            FirstPage()
                        } else {
                            Text(
                                "SecondPage()"
                            )
                        }
                    }
                case .salad: Text(selectedCategory!.localizedName)
                case .sandwich: Text(selectedCategory!.localizedName)
                case .none: Text("hi")
                }
            }.environmentObject(test)
        }
    }
}

@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
    static var previews: some View {
        SplitTestView()
    }
}
struct FirstPage: View {
    @EnvironmentObject var test: Test
    var body: some View {
        Text("First Page (test.name)")
    }
}

5

This is why MREs are important that is why I mentioned it in my first comment, you introduced NavigationSplitView.

Scenario 1

If you are using NavigationSplitView you have to inject the EnvironmentObject to the NavigationSplitView.

NavigationSplitView{
    /*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)

Scenerio 2

When working with just NavigationStack you have to inject on the NavigationStack

NavigationStack{
    /*other stuff that includes a navigationDestination*/
}.environmentObject(navModel)

Scenerio 3 – Deprecated

When working with just NavigationView you have to inject on the NavigationView

NavigationView{
    /*other stuff that includes a NavigationLink*/
}.environmentObject(navModel)

Your Sample

Just move the injection code one line down.

import Combine
import SwiftUI

@available(iOS 16.0, *)
struct SplitTestView: View {
    @State var selectedCategory: Category?
    var categories = Category.allCases
    @StateObject var coordinator = Coordinator3() //<-- Switch to StateObject
    @StateObject var test = Test()
    
    var body: some View {
        NavigationSplitView {
            List(categories, selection: $selectedCategory) { category in
                NavigationLink(category.localizedName, value: category)
            }
        } detail: {
            NavigationStack(path: $coordinator.path) {
                switch selectedCategory {
                case .dessert:
                    Text(selectedCategory!.localizedName)
                case .pancake:
                    VStack {
                        Text("Navigation stack")
                            .padding()
                        NavigationLink("NavigationLink to enter first page", value: Destination.firstPage)
                            .padding()
                        NavigationLink("NavigationLink to enter second page", value: Destination.secondPage)
                            .padding()
                    }
                    .navigationDestination(for: Destination.self) { destination in
                        if destination == .firstPage {
                            FirstPage()
                        } else {
                            Text(
                                "SecondPage()"
                            )
                        }
                    }
                case .salad: Text(selectedCategory!.localizedName)
                case .sandwich: Text(selectedCategory!.localizedName)
                case .none: Text("hi")
                }
            }
        }.environmentObject(test) //<<--- Add to the NavigationSplitView - The NavigationLink's are presenting in a separate column than the Stack, the only thing they share is the split view.
    }
}
enum Destination {
    case firstPage
    case secondPage
}

enum Category: Int, Hashable, CaseIterable, Identifiable, Codable {
    case dessert
    case pancake
    case salad
    case sandwich
    
    var id: Int { rawValue }
    
    var localizedName: LocalizedStringKey {
        switch self {
        case .dessert:
            return "Dessert"
        case .pancake:
            return "Pancake"
        case .salad:
            return "Salad"
        case .sandwich:
            return "Sandwich"
        }
    }
}

@available(iOS 16.0, *)
class Coordinator3: ObservableObject {
    @Published var path = NavigationPath()
    
    func gotoHomePage() {
        path.removeLast(path.count)
    }
    
    func tapOnEnter() {
        path.append(Destination.firstPage)
    }
    
    func tapOnFirstPage() {
        path.append(Destination.secondPage)
    }
    
    func tapOnSecondPage() {
        path.removeLast()
    }
    
    func test() {
        path.removeLast(path.count)
        path.append(2)
    }
}

class Test :ObservableObject {
    var name = "test"
}



@available(iOS 16.0, *)
struct SplitTestView_Previews: PreviewProvider {
    static var previews: some View {
        SplitTestView()
    }
}
struct FirstPage: View {
    @EnvironmentObject var test: Test
    var body: some View {
        Text("First Page (test.name)")
    }
}

Addl Info

In the Migration Guide apple talks about the differences between the 2 types.

https://developer.apple.com/documentation/swiftui/migrating-to-new-navigation-types

They call the inside of the NavigationStack “content”

NavigationStack {
    /* content */
}

And the inside of the NavigationSplitView “columns”

NavigationSplitView {
    /* column 1 */
} content: {
    /* column 2 */
} detail: {
    /* column 3 */
}

In their respective setups the “columns” and “content” only share the NavigationSplitView or NavigationStack respectively with the NavigationLinks/navigationDestination.

A NavigationLink inside NavigationStack that is inside NavigationSplitView presents in its own column.

The injection should always happen at the uppermost shared View.

9

I’ve found that as my projects get more complex, using EnvironmentObjects get a bit buggy. Something I’ve been incorperating is shared singleton classes instead of EnvironmentObjects classes.

To do this, inside of your class, list a property static let shared = NavigationModel(). Then, in any view you want to use that class, reference it in the view properties as @ObservedObject var navModel = NavigationModel.shared.

There are pros and cons to this approach, but I’ve found that the pros outweigh the cons as environment objects tend to be buggy.

9

It is a known behaviour that environmentObjects do not flow through the navigation system. You have to do manual injections every time.

Additionally in the WWDC lounges someone asked:

I’ve had several intermittent crashes from environment objects being
nil when I pass them to a sheet or NavigationLink. […]

The answer was:

NavigationLink by design doesn’t flow EnvironmentObjects through to
its destination as it’s unclear where the environmentObject should be
inherited from. I suspect this might what’s causing your issue. In
order to get the behavior you expect, you’ll have to explicitly pass
the environmentObject through at that point.

https://developer.apple.com/forums/thread/683564

1

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