NEW: Learn to build amazing SwiftUI apps for macOS with my new book! >>

Environment object not being inherited by child (sometimes) and app crashes

Forums > SwiftUI

I'm using the code from Paul's article "How to use @EnvironmentObject to share data between views". I just updated to xcode 11.4. This example crashes every time if you click "Increase Score" first followed by "Show Detail View". If I click "Show Detail View" first, it navigates to the view and I can navigate back. If I do that sequence 3, 4, or 5 times it crashes. The error is "Fatal error: No ObservableObject of type UserSettings found. A View.environmentObject(_:) for UserSettings may be missing as an ancestor of this view."

This example works as expected, however, in xcode 11.3. Anyone else seeing this issue? I noticed it first in my own project that stopped working and found this simple example from Paul as a testbed.

// SceneDelegate class UserSettings: ObservableObject { @Published var score = 0 }

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
var settings = UserSettings()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = ContentView()

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView.environmentObject(settings))
        self.window = window
        window.makeKeyAndVisible()
    }
}

// ContentView struct ContentView: View { @EnvironmentObject var settings: UserSettings

var body: some View {
    NavigationView {
        VStack {
            // A button that writes to the environment settings
            Button(action: {
                self.settings.score += 1
            }) {
                Text("Increase Score")
            }

            NavigationLink(destination: DetailView()) {
                Text("Show Detail View")
            }
        }
    }
}

}

struct DetailView: View { @EnvironmentObject var settings: UserSettings

var body: some View {
    // A text view that reads from the environment settings
    Text("Score: \(settings.score)")
}

}

2      

Hi,

I've the same problem as well. Have you found something so far?

Thanks in advance.

1      

Hi again,

After I found this Paul's tweet (https://twitter.com/twostraws/status/1146315336578469888?s=20) I tried to send environment objects to NavigationLink as well. Now it works. It may help you too. It seems it's a bug and I'm really tired on struggling SwiftUI bugs. :(

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Spend less time managing in-app purchase infrastructure so you can focus on building your app. RevenueCat gives everything you need to easily implement, manage, and analyze in-app purchases and subscriptions without managing servers or writing backend code.

Get Started

Sponsor Hacking with Swift and reach the world's largest Swift community!

This has been bugging me as well. Everything runs fine in my simulator, but the SwiftUI preview does not work and it's frustrating as that is supposed to be one of the perks of SwiftUI.

I get this:

Fatal error: No ObservableObject of type Observer found. A View.environmentObject(_:) for Observer may be missing as an ancestor of this view.: file SwiftUI, line 0

Even though I've passing everything appropriately in the SceneDelegate and in each View Preview

1      

In the SceneDelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
var settings = UserSettings()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = ContentView().environmentObject(settings) // <- add here

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView) // <- remove from here
        self.window = window
        window.makeKeyAndVisible()
    }
}

1      

I have a screenCoordinator class that is defined as an EnvironmentObject in SceneDelegate as follows:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var screenCoordinator = ScreenCoordinator()

    //
    // general code
    //
    let contentView = HomeView(viewModel: viewModel)
                .environment(\.managedObjectContext, context)
                .environmentObject(self.screenCoordinator)

This was breaking a number of my View previews and I managed to fix it by instatiating the 'ScreenCoordinator' class in the preview function as follows:

struct AddItemView_Previews: PreviewProvider {
    @State static var screenCoordinator = ScreenCoordinator()

    static var previews: some View {
        AddItemView()
            .environmentObject(self.screenCoordinator)
    }
}

1      

Did you try to use @StateObject?

@StateObject private var userSettings = UserSettings()

Then pass it as an environmentObject:

contentView.environmentObject(userSettings)

Note: @StateObject is only available in iOS 14 (SwiftUI 2.0).

You can find more info here.

If that didn't work, just try to add self when you pass it:

contentView.environmentObject(self.settings)

1      

We are using SwiftUI "2.0".

Firebase reported a crash with our app: Fatal error: No ObservableObject of type Observer found. A View.environmentObject(_:) for Observer may be missing as an ancestor of this view.: file SwiftUI, line 0.

The user was on iOS 14.01, but I have not been able to reproduce the issue on iOS 14.1 or 14.2. I have not tried 14.01 yet. However as I have seen this error reported in other forums and it seems to be a bug with SwiftUI when environmentObject is not used with a view created by NavigationLink. The recommend solution was just to use environmentObject, but that seems to defeat the purpose of using the environment to store objects in the first place.

Does anyone know of any offical Apple bug fixes where this is addressed? If it's been fixed, then I won't worry about it in older iOS releases.

Thanks.

2      

Late to this conversation, but believe I ran into something similar, and may have found a solution.

Here is the diff on a commit of an example project where the change resulted in the aforementioned crash and error message not occurring.

In short, it appears that when the object is inserted into the environment, it should be attached to the NavigationView, and not the child view within.

For a somewhat less trite example, from a bare bones project made while reading Practical Core Data that has a pretty common list / detail / edit sheet navigation structure.

I effected a similar series of edits – now only inserting the storage provider into the environment once – but I can use it everywhere, including within a view that is presented within a sheet.

Being able to use an object inserted into environment as illustrated in these two examples meshes with my understanding of how this feature of SwiftUI is meant to work. What do others think?

1      

@fkuhl  

My experience matches that of @lcs-rgordon. If the environmentObject is placed on NavigationViews and on Views presented in a sheet. the object is available. Wish this had been written down somewhere.

1      

@fkuhl  

My experience matches that of @lcs-rgordon. If the environmentObject is placed on NavigationViews and on Views presented in a sheet. the object is available. Wish this had been written down somewhere.

1      

I had the warning,[Assert] displayModeButtonItem is internally managed and not exposed for DoubleColumn style. Returning an empty, disconnected UIBarButtonItem to fulfill the non-null contract. It also crashed after navigating 3-4 times. Xcode 12.5.1

After setting .navigationViewStyle(StackNavigationViewStyle()) on NavigationView the crash was not happen anymore.

2      

Then I remember the numbers. If my baby were to be born today, they would be 10 years old when a quarter of the world’s insects could be gone, when 100 million children are expected to be suffering extreme food scarcity. My child would be 23 when 99% of coral reefs are set to experience severe bleaching. They would be 30 – my age now – when 200 million climate refugees will be roaming the world, when half of all species on Earth are predicted to be extinct in the wild. They would be 80 in 2100, when parts of Australia, Africa and the United States could be uninhabitable dll download

We are in the middle of a mass extinction, the first caused by a single species. There are 7.8 billion of us, on a planet that scientists estimate can support 1.5 billion humans living as the average US citizen does today. And we know that the biggest contribution any individual living in affluent nations can make is to not have children. According to one study, having one fewer child prevents 58.6 tonnes of carbon emissions every year; compare that with living car-free (2.4 tonnes), avoiding a transatlantic return flight (1.6), or eating a plant-based diet (0.82). Another study said it was almost 20 times more important than any other choice an environmentally minded individual could make. Such claims have been questioned. After all, does a parent really bear the burden of their child’s emissions? Won’t our individual emissions fall as technologies and lifestyles change? Isn’t measuring our individual carbon footprint – a concept popularised by oil and gas multinational BP – giving a free pass to the handful of corporate powers responsible for almost all carbon emissions? The only thing that isn’t up for debate is that we all know that we are living in ways that can’t continue.

1      

https://www.hackingwithswift.com/forums/swiftui/environment-object-not-being-inherited-by-child-sometimes-and-app-crashes/269/9551

@ccalislar note about setting .navigationViewStyle(StackNavigationViewStyle()) on NavigationView actually fixed the issue.

Looks like default navigation view style swaps visible content by extracting a destination from NavigationLink and removing whatever was outside of NavigationLink with exception of NavigationView itself. Thus, if we have nested views that are all wrapped with a NavigationView and we set environment objects on a view that is the direct child of NavigationView like in this example:

NavigationView {
    VStack {
        ...
        NavigationLink(destination: DetailView()) {
            Text("Go on, click me")
        }
    }
    .environmentObject(someSharedObject)
}

we will loose access to environmentObject because VStack with all its contents will be swapped to DetailView as soon as we click on a Text. That is the only reason I can think of that also explains why using .navigationViewStyle(StackNavigationViewStyle()) actually fixes the issue.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Spend less time managing in-app purchase infrastructure so you can focus on building your app. RevenueCat gives everything you need to easily implement, manage, and analyze in-app purchases and subscriptions without managing servers or writing backend code.

Get Started

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.