The timeline you’ve created can show only one view. Letting the caller specify what to display for each item would be more helpful. That’s where SwiftUI’s ViewBuilder comes in.
You’ve already been using view builders since starting to learn SwiftUI. They are wrappers Apple provides that simplify creating views by composing smaller views into a single view. This wrapper is also why your SwiftUI views don’t need to contain return statements for each view. And every time you’ve used a SwiftUI view such as VStack or ForEach that uses a closure, you used a view builder.
Here’s the initial code in FlightTimelineView.swift that builds the timeline:
ForEach(flights) { flight in
FlightCardView(flight: flight)
}
You pass FlightCardView to the closure of the ForEach loop. ForEach uses a view builder to create a parameter for the view-producing closure. Just like the SwiftUI framework uses view builders, you can create custom views yourself that use ViewBuilder. In this segment, you’ll use this technique to transform the flight timeline into a flexible and reusable view.
Create a new SwiftUI view named GenericTimelineView in the Timeline group. First, update the struct definition to the following:
struct GenericTimelineView<Content>: View where Content: View {
You add a generic type Content and then constrain it with the where keyword so it must be a View. This change allows GenericTimeline to accept View values as dependencies. Add the following properties to the top of GenericTimelineView:
// 1
let flights: [FlightInformation]
let content: (FlightInformation) -> Content
This new GenericTimelineView view needs two properties. First, you store the list of FlightInformation objects as flights. You also add a property to contain the closure passed to the view.
You create a custom initializer that takes an array of FlightInformation objects through the flights parameter. The @ViewBuilder at the start of the following parameter marks it as a closure the caller will pass in. The initializer’s body assigns the method’s parameters to the properties you added above. You also define that the closure will take an object of type FlightInformation that you can access within the view passed as the closure.
Now, change the body to:
var body: some View {
ScrollView {
VStack {
ForEach(flights) { flight in
content(flight)
}
}
}
}
Notice how this view appears almost the same as the original FlightTimelineView. You iterate over the FlightInformation array passed in using the flights property as you would in any other view. The key difference comes in this line:
content(flight)
You stored the closure passed into this view in the content parameter. Here, you use that view and display it with each iteration through the loop, passing the flight for this iteration into the view.
To see this in action, update the preview to:
let testFlights = FlightData.generateTestFlights(date: Date())
return GenericTimelineView(flights: testFlights) { flight in
FlightCardView(flight: flight)
}
This preview creates a list of test flights and then calls your new view. This call uses the initializer you created above, placing the list of flights into the flights property. You then pass the closure, which your initializer stores in the content property. Because you defined the closure as having a FlightInformation-typed parameter, you give the name flight for this value passed into the closure. Finally, you show the FlightCardView passing that flight view, which SwiftUI passed into the closure.
This code produces the same result as the original code:
However, unlike the original code, you can pass any view into the closure without creating a view. Now, navigate to FlightTimelineView.swift. Find that block of code that creates the list of views. Replace it with:
GenericTimelineView(flights: flights) { flight in
FlightCardView(flight: flight)
}
Take a moment to appreciate what you’ve created here. Instead of hard coding the list view behavior in FlightTimelineView, you’re now using a generic view that does this for you. Although the view is simple right now, it can work to display a list of any type of view.
Run the app and verify that the timeline looks the same as before. Although there’s no change in appearance, you’ve gained a more flexible way to choose the view to show for each flight.
Timeline with enclosed view
Although this change makes it easier to specify different views, it’s still tied to the FlightInformation structure, preventing reuse in other projects. In the next segment, you’ll address that limitation.
See forum comments
This content was released on Mar 12 2025. The official support period is 6-months
from this date.
Harness the power of SwiftUI’s ViewBuilder to create reusable views.
Download course materials from Github
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Previous: Showing Flight Progress
Next: Making the Timeline Generic
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.