SwiftUI has been officially released with Xcode 11 and in this introductory step by step tutorial we are going to build an app which lists hiking trails near Silicon Valley.
Get started by creating new Xcode project using Single View App template, name it “HikingSiliconValley” and make sure to select “SwiftUI” as User Interface.

Get yourself familiar with the newly created project structure, run it in the Simulator and then remove the following line of code from the ContentView:
Text("Hello World")
First, create a SwiftUI List of few static items to get our proof of concept going. Your ContentView should look like this:
struct ContentView: View {
var body: some View {
List {
Text("Stanford Dish")
Text("Edgewood")
Text("Mission Peak")
Text("Big Basin")
Text("Alum Rock")
}
}
}
Build and run your app. The result should look just like a UITableView:

On top of knowing the name of the hike, we’d like to keep track of its location and distance. Because SwiftUI is very flexible, each of the List rows can be of a different View type. Let’s take advantage of this fact and prototype the first row to contain more information about the Stanford Dish hike to make it look like this:

We are going to convert the row into HStack to accommodate left and right row details and then make the left detail a VStack to add hike location. In previous posts, we learned how to work with stacks in SwiftUI and how to further customize appearance of text using modifiers. Spacer() expands empty space between stack elements to push them further apart and in our case create left and right detail elements in list row.
The resulting code should look like this:
List {
HStack {
VStack(alignment: .leading) {
Text("Stanford Dish")
Text("Palo Alto").font(.subheadline).foregroundColor(.gray)
}
Spacer()
Text("3.9 miles")
}
Text("Edgewood")
Text("Mission Peak")
Text("Big Basin")
Text("Alum Rock")
}
At this point we need to refactor the code and make four things:
- Create a custom struct to represent hiking trail objects – Trail.
- Create a data structure (array) to hold sample data – hikingTrails.
- Create a brand new view which can be re-used to represent custom List rows – TrailRow.
- Build a list of trails from the hikingTrails array and re-use the newly created TrailRow.
Add the following code above the declaration of ContentView to create custom Trail struct:
struct Trail: Identifiable {
var id = UUID()
var name: String
var location: String
var distance: Double
}
It is very important to note that if you want to use a data structure to represent a row in a List, it has to conform to the Identifiable protocol and implement a single requirement – a unique ID, for the List to be able to uniquely distinguish between all of its rows.
Now, use the newly created Trail structure to populate array which will provide data to the List. Add this code inside of the ContentView declaration, at the very top:
let hikingTrails = [
Trail(name: "Stanford Dish", location: "Palo Alto", distance: 3.9),
Trail(name: "Edgewood", location: "Redwood City", distance: 3.2),
Trail(name: "Mission Peak", location: "Fremont", distance: 7.1),
Trail(name: "Big Basin", location: "Boulder Creek", distance: 4.3),
Trail(name: "Alum Rock", location: "Milpitas", distance: 5.7),
]
Create a new TrailRow view. Add this code above the declaration of ContentView:
struct TrailRow: View {
var trail: Trail
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(trail.name)
Text(trail.location).font(.subheadline).foregroundColor(.gray)
}
Spacer()
Text(String(format: "%.1f miles", trail.distance))
}
}
}
Update the ContentView code to iterate over the hikingTrails array and create the List:
struct ContentView: View {
let hikingTrails = [
Trail(name: "Stanford Dish", location: "Palo Alto", distance: 3.9),
Trail(name: "Edgewood", location: "Redwood City", distance: 3.2),
Trail(name: "Mission Peak", location: "Fremont", distance: 7.1),
Trail(name: "Big Basin", location: "Boulder Creek", distance: 4.3),
Trail(name: "Alum Rock", location: "Milpitas", distance: 5.7),
]
var body: some View {
List(hikingTrails) { trail in
TrailRow(trail: trail)
}
}
}
Build and run your Hiking Trails app. Congratulations, you’ve just build your first SwiftUI app!
For reference, the entire contents of ContentView.swift file should look like this:
import SwiftUI
struct Trail: Identifiable {
var id = UUID()
var name: String
var location: String
var distance: Double
}
struct TrailRow: View {
var trail: Trail
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(trail.name)
Text(trail.location).font(.subheadline).foregroundColor(.gray)
}
Spacer()
Text(String(format: "%.1f miles", trail.distance))
}
}
}
struct ContentView: View {
let hikingTrails = [
Trail(name: "Stanford Dish", location: "Palo Alto", distance: 3.9),
Trail(name: "Edgewood", location: "Redwood City", distance: 3.2),
Trail(name: "Mission Peak", location: "Fremont", distance: 7.1),
Trail(name: "Big Basin", location: "Boulder Creek", distance: 4.3),
Trail(name: "Alum Rock", location: "Milpitas", distance: 5.7),
]
var body: some View {
List(hikingTrails) { trail in
TrailRow(trail: trail)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related tutorials:
- How to customize List in SwiftUI with sections, header and footer
- How to add NavigationView to List in SwiftUI and show detail view using NavigationLink
- How to add button to navigation bar in SwiftUI