How to make Navigation in SwiftUI a piece of cake

If you are used to functional programming with UIKit, it takes some time to wrap your head around SwiftUI. This is especially true for navigation. Storyboards and Navigation Segues are just so essential. But after a couple of months I have finally got the hang of it. So I will share some very useful design patterns and SwiftUI bugs to avoid with you.

This is not a beginner tutorial. If you need to review NavigationView, TabView and SplitViews you can start with this great RayWenderlich tutorial. I also assume you have seen @State, @EnvironmentObject and @Published before.

What is the state of Navigation?

The var defining the state is the selection in line 4. It is either 0 for “first view” or 1 for “second view”.

If now look at NavigationView with NavigationLink, the state is defined by the isActive of the link:

If you are using NavigationLink(destination: , label:) SwiftUI will keep the state var hidden from you. But it is still there. Next example is alert, where again we use @State to mange what views are shown.

The class that rules them all

This class needs to use ObservableObject, so I can use it in the environment. In order to place it in the environment and access my NavigationController vars anywhere, we will place it at the very top most view level. That is in the SceneDelegate. This works in multi window, because each window has it’s own instance of SceneDelegate and thus it’s own NavigationController. Changing SceneDelegate to:

We now need to bind the navigation views to the NavigationController, so it holds all the information. This means we need to bind the selection of TabView and the NavigationLink active State.

Creating shortcuts for the user

As an example you might have a subscription model included in your app. The user wants to unlock the pro feature and you want to bring them to the subscription section of your app. So easy for you now.

Collapsing NavigationStacks

Reacting to the backend

There is a lot to discuss in the following code snippit. So I will highlight the most important design patterns. The login state is managed by a singleton class AuthSubscriptionManager. Since I want to access the same information from multiple windows, I do not want to use an EnvironmentObject. The singleton pattern gives me one AuthSubManager instance for the whole app. The login is done with FirebaseAuth. When the var firebaseUser changes, I want to update my UI. In swiftUI this is easily done with Combine (I am not a fan of NotificationCenter, but it would work, too). So in the NavigationController we subscribe to any changes to the firebase user (line 13). When this happens we call resetAll(), where the navigation state vars are set back. You can update the UI state depending on your wishes.

Take the user right back

In the main content view, we check if the user has seen the onboarding before. If not we show the OnboardingView. When the user taps on the “done” button we change the hasSeenOnboarding to true, which is saved in Userdefaults.

You can use this pattern to control a whole onboarding flow. I also use this pattern to ask the user to review your app after x-number of app launches.

Some problems that need mentioning

Take away

I have a Ph.D. in physics, during which I started to love software engineering. I have deveped my own app with SwiftUI and I created a SwiftUI online course.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store