
Another WWDC has come and gone, leaving in its wake a glut of new features and enhancements. Now that the initial excitement has passed, software developers across the world are thinking about how they can update their apps to benefit from these exciting new offerings. Improvements and evolution come at a cost, with API modernization accompanied by deprecations. SwiftUI is no exception in both regards, its updates offering both benefits and challenges to developers.
One of the most notable updates for SwiftUI is its navigation system. Navigation in SwiftUI has been a pain point for its users, especially with regard to bigger code bases, and Apple’s attention to navigation in this update will put a smile on the faces of many developers. But with this overhaul come new warnings and deprecations. Let’s take a look at SwiftUI’s navigation updates in practice and explore how to implement its features.
Putting SwiftUI Navigation to the Test
Apple’s examples for new updates are usually on the topic of food, but I’m more of a reader than a chef. We’ll build an application called Book Worm using SwiftUI’s new navigation features starring a collection of my favorite programming books.
We can understand what’s new in the field of SwiftUI navigation by building a simple app. The starting point of the app is a simple scrollable list. Tapping on a list item reveals the corresponding book details. From there, a third layer of the UI offers more in-depth information on that specific book.
Without further ado, let’s dive into coding.
Step One: Book List
As a warm-up, let’s create BookListCellView that will represent a single element of the book list. We don’t need anything complicated here, just a couple of text labels:
Stack(alignment: .leading) {
Text(viewModel.authorsString)
Text(viewModel.titleString)
.font(.headline)
}
.padding([.leading, .trailing])
I prefer to have separate view models for my views. Some people argue that it’s not a good idea, but I try to keep as much business and presentation logic out of the view layer as possible, because it helps unit testing and abstracting out layers.
In Book Worm all view models are pretty straightforward, but let’s also look at an underlying Book model that is at the heart of the data service of the app. You can now see what information the app shows to the end user:
struct Book: Decodable {
let authors: [Author]
let brief: String
let id: String
let title: String
let year: String
}
struct Author: Decodable {
let id: String
let name: String
let brief: String?
}
And here’s the body of the list view, BookListView, itself:
List(0..<viewModel.booksCount, id: \.self) {
BookListCellView(
viewModel: viewModel.cellViewModel(forIndex: $0)
)
}
The MainView of the app only has the list for now:
var body: some View {
BookListView(viewModel: viewModel.bookListViewModel)
}
This is how it looks on iOS:

Figure 1: The book list on iOS
Not bad. Now let’s implement something more interesting.
