Setting Up a Navigation Stack
Open the starter project for this chapter, and you’ll find a very early version of the flight-data app for an airport that you’ll use for this module. You’d likely get the flight information from an external API in a real-world app. For this lesson, you’ll use mock data created in the FlightData.swift file inside the Models folder.
The FlightData class generates a schedule for fifteen days of flights with thirty flights per day starting with today’s date using the generateSchedule() method. The class uses a seeded random number generator to produce consistent flight data every time, with only the start date changing. Now, open and examine FlightInformation.swift, which encapsulates information about flights you’ll display in this module.
Creating the Navigation List
Open WelcomeView.swift. The view includes a @StateObject named flightInfo that holds this mock data for the app.
Build and run the starter app. You’ll see a bare-bones implementation with a graphic and a single option to view the day’s flight status board.
In this app, you’ll see a list of flights. You can then select a flight and see more information about it. This pattern of choosing from a list of items to see more about that item is known as hierarchical navigation. The user can also return to an earlier view in the navigation stack. Lower views in the stack can also present multiple items and move further in the hierarchy. Hierarchical navigation works well when the user has little need to switch laterally between views where navigation flows from broader to more specific information at each level.
In SwiftUI, you implement hierarchical navigation using a NavigationStack. A hierarchical layout has fewer top-level views than a flat layout, but each contains a more in-depth view stack beneath. The user may also have to backtrack through several layers of the navigation stack to find another view. Since you have a set of flights to display, you’ll build a List to show them to the user.
Before the Spacer() at the end of the view, add the following code:
// 1
List(flightInfo.flights) { flight in
// 2
Text(flight.statusBoardName)
}
// 3
.listStyle(.plain)
Here’s how this code displays the flights to the user:
- You display a list that iterates over a set of data, here an array of
FlightInformationcontained in theflightsproperty of theflightInfostate. TheFlightInformationstruct implements theIdentifiableprotocol, which requires a uniqueidproperty. SwiftUI uses this to distinguish between the different items for navigation. - You display the name and other end of the flight using the
statusBoardNameproperty. - Then, you specify the
plainlist style, which removes some default formatting and will show more text on the view.
Build and run the app. Now you’ll see a list of flights.
Now that you can show a list of flights, in the next section, you’ll add the ability to view more flight details.
Adding Navigation to a List
First, you’ll tell SwiftUI you want to use a List for navigation by wrapping it inside a NavigationStack. In WelcomeView.swift, change the initial VStack at the top of the view to:
NavigationStack {
This change will wrap the entire view inside the navigation structure. Only the part of the view inside the NavigationStack becomes part of the navigation structure. In most cases, dedicating the whole view to navigation provides the best results.
It’s not required, and you could leave part of the view as a header if you only wrapped the List inside the NavigationStack.
Using a NavigationStack tells SwiftUI you want to implement hierarchical navigation. Next, you need to tell SwiftUI what parts of the view trigger navigation and how to react when the user interacts with those elements.
The starter app already contains a simple view showing details for the flight. Inside the FlightDetails group, open FlightDetails.swift. This view shows a few details about a flight passed to it.
Now back in WelcomeView.swift, change the list to:
List(flightInfo.flights) { flight in
// 1
NavigationLink(flight.statusBoardName, value: flight)
}
// 2
.navigationDestination(
// 3
for: FlightInformation.self,
// 4
destination: { flight in
FlightDetails(flight: flight)
}
)
These changes add support for navigation to the list’s rows. Here’s how they work:
- This
NavigationLinkview uses a convenience initializer that displays a string in aTextview. Thevalueparameter tells SwiftUI to send theflightproperty passed to the closure for theListassociated with this row to the next navigation component in the next step. - In step one, you told SwiftUI how to display a navigation link and defined the value SwiftUI associates with that row. The
navigationDestination(for:destination:)modifier tells SwiftUI what to do with the value. This modifier connects values of a specified type with an action to perform. Notice that you apply it to theList. The modifier will only apply toNavigationLinks inside the List. Don’t place the modifier inside a looping container likeListorScrollView. - The
forparameter tonavigationDestination(for:destination:)specifies the type ofvaluethenavigationDestination(for:destination:)handles. PassingFlightInformation.selftofortells SwiftUI to use this method for values of theFlightInformationtype. In step one, you passed an instance of aFlightInformationstruct through thevalueparameter. SwiftUI connects the two and will use thisnavigationDestination(for:destination:)for selectedNavigationLinks. - The
destinationparameter tells SwiftUI what view to show when it’s passed the matching type. SwiftUI passed aFlightInformationinstance calledflightthat matches that passed to thevalueparameter in step one. You display theFlightDetailsview passing along theflight.
The preview will show you the new list. On iOS, you’ll get the small right-pointing disclosure arrow at the end of each row. This visual indicator shows the user that tapping the row will lead to more information, and SwiftUI automatically adds it to the List inside a NavigationStack:
Run the app and tap any flight. You’ll see the details for that flight.
Those are the basics of building a view navigation in SwiftUI:
- You wrap the navigable parts of the app inside a navigation view such as
NavigationStack. - Then, you define the elements of the view that trigger navigation inside a
NavigationLinkview that defines what to display and the value associated with the display. - You then apply a
navigationDestinationmodifier that tells SwiftUI what to do when the user selects aNavigationLink. - You can mix
NavigationLinks of different types by providingnavigationDestinationmodifiers for each type, allowing a single element to perform various actions based on the kind of data.
Next, you’ll explore ways to customize the navigation and help users keep their place inside the navigation stack.