November 22nd, 2023

Building an airline seat map in SwiftUI

I've been asked to do a tutorial about building an airline seat map in SwiftUI many times. This has been my go-to snippet for years! It's super customizable and provides an awesome starter for any iOS developer in aviation.

Max Alexander

Max Alexander

Co-Founder/Chief Product Officer

If you've ever booked a flight, you're familiar with the seat map where you choose your preferred seat. In this tutorial, we'll replicate this feature using SwiftUI.

Step 1: Define the Data Model

Let's start by defining our data model:

enum SeatState {
    case empty
    case occupied
}

struct SeatId: Equatable, Hashable {
    var row: Int
    var letter: String
}

struct Seat: Identifiable, Equatable {
    var id: SeatId
    var state: SeatState
}

Here, each Seat has an id (consisting of its row and letter) and a state (which can be either empty or occupied).

Step 2: Create the Seat View

We need a visual representation of our seat:

struct SeatView: View {
    var seat: Seat

    var body: some View {
        Text(seat.id.letter)
            .frame(width: 40, height: 40)
            .background(seat.state == .empty ? Color.green : Color.red)
            .cornerRadius(8)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(Color.black, lineWidth: 2)
            )
    }
}

The SeatView displays the seat letter and changes its background based on its state.

Step 3: Build the Seat Map

Our goal is a grid layout with 2 seats, an aisle with a row number, 3 seats, another aisle, and then 2 more seats:

struct FirstClassView: View {

    @State var seats: [Seat] = (1..<25).map { row in
        ["A", "B", "C", "D", "E", "F", "G"].map { letter in
            Seat(id: SeatId(row: row, letter: letter), state: .empty)
        }
    }.flatMap { $0 }

    var columns: [GridItem] = [
        .init(.flexible()),  // A seat
        .init(.flexible()),  // B seat
        .init(.fixed(40)),   // Aisle with row number
        .init(.flexible()),  // C seat
        .init(.flexible()),  // D seat
        .init(.flexible()),  // E seat
        .init(.fixed(40)),   // Aisle with row number
        .init(.flexible()),  // F seat
        .init(.flexible())   // G seat
    ]

    var body: some View {
        ScrollView([.vertical, .horizontal], showsIndicators: true) {
            LazyVGrid(columns: columns, spacing: 20) {
                ForEach(seats) { seat in
                    switch seat.id.letter {
                    case "A", "B", "C", "D", "E", "F", "G":
                        SeatView(seat: seat)
                            .onTapGesture {
                                toggleSeat(seat: seat)
                            }
                    default:
                        Spacer()
                    }

                    if seat.id.letter == "B" || seat.id.letter == "E" {
                        Text("\(seat.id.row)")
                            .font(.headline)
                            .frame(width: 40, height: 40)
                    }
                }
            }
            .padding()
        }
    }

    func toggleSeat(seat: Seat) {
        if let index = seats.firstIndex(of: seat) {
            seats[index].state = seats[index].state == .empty ? .occupied : .empty
        }
    }
}

With SwiftUI, building complex layouts becomes a breeze. Our seat map, with different configurations and aisles, is just a demonstration of how adaptable and flexible SwiftUI grids can be.

Remember, you can further customize the appearance, add more features like zooming, selecting multiple seats, or fetching seat availability from a server. The possibilities are limitless! Happy coding!

Get posts in your inbox

Subscribe to updates and we'll send you occasional emails with posts that we think you'll like.