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.

Initial app
Initial app

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:

  1. You display a list that iterates over a set of data, here an array of FlightInformation contained in the flights property of the flightInfo state. The FlightInformation struct implements the Identifiable protocol, which requires a unique id property. SwiftUI uses this to distinguish between the different items for navigation.
  2. You display the name and other end of the flight using the statusBoardName property.
  3. Then, you specify the plain list 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.

Displaying the list of flights
Displaying the 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.

Simple Flights Details View
Simple Flights Details View

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:

  1. This NavigationLink view uses a convenience initializer that displays a string in a Text view. The value parameter tells SwiftUI to send the flight property passed to the closure for the List associated with this row to the next navigation component in the next step.
  2. 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 the List. The modifier will only apply to NavigationLinks inside the List. Don’t place the modifier inside a looping container like List or ScrollView.
  3. The for parameter to navigationDestination(for:destination:) specifies the type of value the navigationDestination(for:destination:) handles. Passing FlightInformation.self to for tells SwiftUI to use this method for values of the FlightInformation type. In step one, you passed an instance of a FlightInformation struct through the value parameter. SwiftUI connects the two and will use this navigationDestination(for:destination:) for selected NavigationLinks.
  4. The destination parameter tells SwiftUI what view to show when it’s passed the matching type. SwiftUI passed a FlightInformation instance called flight that matches that passed to the value parameter in step one. You display the FlightDetails view passing along the flight.

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:

Navigation Enables List with Disclosure Arrows
Navigation Enables List with Disclosure Arrows

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 NavigationLink view that defines what to display and the value associated with the display.
  • You then apply a navigationDestination modifier that tells SwiftUI what to do when the user selects a NavigationLink.
  • You can mix NavigationLinks of different types by providing navigationDestination modifiers 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.

See forum comments
Download course materials from Github
Previous: Introduction Next: Demo