Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fatal error: No observable object of type NavigationStack.Type found (iOS 13.0) #11

Closed
Faltenreich opened this issue Mar 9, 2020 · 5 comments

Comments

@Faltenreich
Copy link

Hello there!

First of all, thank you for this amazing library which fixed many problems we had while implementing a navigation stack on our own, e.g. when using conditional navigation via switch-case-statements.

While this library is working completely fine on iOS 13.1, 13.2.2 and 13.3, it leads to the following crash during app start on iOS 13.0:

Fatal error: No observable object of type NavigationStack.Type found. A View.environmentObject(_:) for NavigationStack.Type may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-24.4/Core/EnvironmentObject.swift, line 161 Fatal error: No observable object of type NavigationStack.Type found. A View.environmentObject(_:) for NavigationStack.Type may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-24.4/Core/EnvironmentObject.swift, line 161 (lldb)

There seems to be a problem with attaching NavigationStack as an EnvironmentObject. We were able to fix this issue by setting it as an EnvironmentObject by our own. This though is not possible with the library as it is, since its initializes is internal and therefor not accessible.

My question is now how to fix this issue on iOS 13.0. If there is no solution, maybe you could provide a public initializer as a workaround, so we can add the EnvironmentObject by our own.

Thanks in advance and best regards,
Philipp

@matteopuc
Copy link
Owner

Hi @Faltenreich, thanks for your support. I've just managed to try the NavigationStackView on iOS 13.0 and I can't manage to reproduce your issue. I tried with this minimum example:

import SwiftUI
import NavigationStack

struct ContentView : View {
    var body: some View {
        NavigationStackView {
            View1()
        }
    }
}

struct View1: View {
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)

            PushView(destination: View2()) {
                Text("PUSH")
            }
        }
    }
}

struct View2: View {
    @EnvironmentObject var navStack: NavigationStack

    var body: some View {
        ZStack {
            Color.yellow.edgesIgnoringSafeArea(.all)
            
            Button(action: {
                self.navStack.pop()
            }, label: {
                Text("POP")
            })
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The example exploits both a PushView and a programmatic transition to trigger a pop operation. And it works as expected:

Mar-11-2020 16-41-39

Can you provide me with a minimum viable example that causes the problem in order for me to help you debugging this issue? Thank you again.

Matteo

@matteopuc
Copy link
Owner

Hi @Faltenreich, any updates on this issue? Thanks for your help.

@Faltenreich
Copy link
Author

Faltenreich commented Mar 16, 2020

Hi @matteopuc, thank you for your quick response!

Our code looks something like that:

import SwiftUI
import NavigationStack

struct ContentView: View {

    @EnvironmentObject private var navigationStack: NavigationStack
    @ObservedObject private var viewModel = ContentViewModel()

    var body: some View {
        NavigationStackView(transitionType: .none) {
            viewModel.getContentView()
                .frame(minWidth: .zero, maxWidth: .infinity, minHeight: .zero, maxHeight: .infinity)
                .transition(.slide)
        }
    }
}

The ContentView itself is nested within some more Views. It contains one dynamic View that is conditionally generated through the ContentViewModel as @published property. Any other View is pushed via the NavigationStack.push() method which is accessed via the EnvironmentObject.

Sadly I am not able to give you a full minimum viable example, since I am currently not at work and have no Apple hardware on my own in order to test it thoroughly.

@matteopuc
Copy link
Owner

Hi @Faltenreich, thanks for replying and for posting this snippet that shows the issue. The problem is that: in SwiftUI you can access an @EnvironmentObject property only within the view hierarchy where the environment object has been injected. In this case you are trying to access the navigation stack

@EnvironmentObject private var navigationStack: NavigationStack

outside the navigation stack hierarchy, which begins with:

NavigationStackView(transitionType: .none) {
    viewModel.getContentView()
        .frame(minWidth: .zero, maxWidth: .infinity, minHeight: .zero, maxHeight: .infinity)
        .transition(.slide)
    }

You just cannot access the navigation stack outside the hierarchy created by the NavigationStackView. You need to wrap your content view in a NavigationStackView, something like:

struct MyRootView: View {
    var body: some View {
        NavigationStackView {
            ContentView()
        }
    }
}

struct ContentView : View {
    @EnvironmentObject private var navigationStack: NavigationStack
    @ObservedObject private var viewModel = ContentViewModel()

    var body: some View {
        viewModel.getContentView()
            .frame(minWidth: .zero, maxWidth: .infinity, minHeight: .zero, maxHeight: .infinity)
            .transition(.slide)
    }
}

Now in your content view you can access the navigation stack.
If you want you can even use the NavigationStackView as your very root just changing a little bit the SceneDelegate file:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let contentView = NavigationStackView {
            ContentView()
        }

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

@Faltenreich
Copy link
Author

Hi @matteopuc,

you are absolutely right: this seems like a bug on our side. The EnvironmentObject within the ContentView even seems a bit useless, since it is not used at all. Does the declaration itself lead to this crash - and why only on iOS 13.0 and not on 13.1+?

Either way, I will close this issue and thank you for your engagement. Your library is highly appreciated! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants