TechnologiesSwiftUI

EmptyTableRowContent struct

iOSmacOStvOSwatchOSvisionOS✓ renders

A table row content that doesn't produce any rows.

How it works

EmptyTableRowContent is the table-row counterpart to EmptyView: a zero-row value that conforms to TableRowContent and contributes nothing to a table. SwiftUI inserts it automatically wherever a table's row builder produces no rows, so a conditional rows: closure always resolves to a concrete TableRowContent type even when a branch is empty. You rarely name it directly; instead you encounter it as the inferred result of an if without an else, or as the value a row builder yields when there is nothing to show. Reach for it conceptually when you reason about why a Table compiles with conditional rows and renders a header with no body.

  1. Provide rows through a result-builder closure

    A Table's trailing rows: closure is a @TableRowBuilder, so its branches must each evaluate to a TableRowContent value. In the example the closure wraps ForEach(people) yielding TableRow(person), and that ForEach is the content for the populated case.

  2. Let an absent else branch become EmptyTableRowContent

    When an if showRows { ... } has no else, the builder supplies EmptyTableRowContent for the missing branch. That is what makes the if showRows block here type-check as a single TableRowContent: the true path is the ForEach, the implicit false path is the empty value, exactly as the // else branch yields EmptyTableRowContent comment notes.

  3. Rely on its TableRowContent and TableRowValue conformance

    EmptyTableRowContent conforms to TableRowContent and carries a TableRowValue, so it slots into the same position as TableRow(person) without changing the row type the Table(of: Person.self) expects. Its TableRowValue is inferred from context, keeping the table's element type consistent as Person across both branches.

  4. Render the columns but no data rows

    Because EmptyTableRowContent produces no rows, the Table still builds its TableColumn("Name", value: \.name) and TableColumn("Role", value: \.role) headers, but the body stays empty. Toggling showRows off swaps the populated ForEach for the empty value, leaving the column headers visible with nothing beneath them.

Try it — Launch with showRows set to false and watch the Name and Role headers appear over an empty body, then flip the Toggle to replace the implicit EmptyTableRowContent with the two TableRow entries.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

EmptyTableRowContent.swift
struct EmptyTableRowContentDemo: View {
    struct Person: Identifiable {
        let id = UUID()
        let name: String
        let role: String
    }

    @State private var showRows = false

    private let people = [
        Person(name: "Ada", role: "Engineer"),
        Person(name: "Linus", role: "Maintainer")
    ]

    var body: some View {
        VStack {
            Toggle("Show rows", isOn: $showRows)
                .padding(.bottom)

            Table(of: Person.self) {
                TableColumn("Name", value: \.name)
                TableColumn("Role", value: \.role)
            } rows: {
                if showRows {
                    ForEach(people) { person in
                        TableRow(person)
                    }
                }
                // else branch yields EmptyTableRowContent
            }
        }
        .padding()
    }
}
Live preview
Show rows Name Role
Show rows Name Role
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →