swiftuiviewcontrolleruinavigationcontrollerswiftuipushviewcontroller

SwiftUI - Fatal Error When Using 'pushViewController'


Because SwiftUI has such a stifling navigational system, I'm attempting to use pushViewController within my SwiftUI views.

However, when I run the code and press the button, I get the following error -

Fatal error: Unexpectedly found nil while unwrapping an Optional value:
file /Users/.../Conjugate/Conjugate/Pages/Practice/PracticeView.swift, line 93

Here is my code -

PracticeView.swift

...
Button(action: {    
             /* Line 93 */ UIApplication.shared.windows[0].rootViewController?.navigationController!.pushViewController(UIHostingController(rootView: ResultView()), animated: true)
}) { ... }
...

SceneDelegate.swift

...
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)

        let nonEmbeddedViewController = UIHostingController(rootView: PracticeView(verb: verbData.shuffled(), numberOfQuestions: CGFloat(verbData.count)))
        let navigationController = UINavigationController(rootViewController: nonEmbeddedViewController)

        window.rootViewController = navigationController

        self.window = window
        window.makeKeyAndVisible()
    }
}
...

Does anybody know how to fix this? I suspect that the navigationController has a value of nil when unwrapped, but I don't know the solution. Thank you!

EDIT - Clarification

I'm trying to make an educational app that quizzes you on a certain topic. The "practice view" is where the user answers questions, which get replaced every time they press the button in the bottom right corner (the one I mentioned in my question). However, when all the questions have been answered, the button needs to open another view (the "result view") instead of just switching the text in the current view. In addition, the navigation bar must be hidden in both the practice view and the result view, and modal sheets won't do. If you need a reference, I guess Duolingo or this slideshow could be useful.


Solution

  • Code markdown may help you for this. Ignoring the SwiftUI navigation view, let go with a model that need to show either (a) a practice view (with an array of questions) or (b) the results. If you set up your model with:

    @Published var currentQuestion:Question
    @Published var showResults:Bool
    

    (This is very much pseudo-code!)

    You can actually have a (simple) content view (again, pseudo-code) that is:

    struct ContentView: View {
        @EnvironmentObject var model: Model
    
        var body: some View {
            if model.showResults {
                ShowResults()
            } else {
                ShowCurrentQuestion()
            }
        }
    }
    

    ShowResults and ShowCurrentQuestion are Views. You can do all kinds of animation (the default is to fade in/out) between the two, and there's absolutely no need for "navigation". As long as your model drives ContentView, it works.

    And yes, I'm not addressing NavigationView - while I'm 'old school" with regards to UINavigationController, the SwiftUI app I'm working on doesn't need anything similar to push/pop or segues. BUT... I am using what I've just described.)