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.
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)yieldingTableRow(person), and that ForEach is the content for the populated case.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 theif showRowsblock 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 EmptyTableRowContentcomment notes.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 theTable(of: Person.self)expects. Its TableRowValue is inferred from context, keeping the table's element type consistent as Person across both branches.Render the columns but no data rows
Because EmptyTableRowContent produces no rows, the
Tablestill builds itsTableColumn("Name", value: \.name)andTableColumn("Role", value: \.role)headers, but the body stays empty. TogglingshowRowsoff swaps the populated ForEach for the empty value, leaving the column headers visible with nothing beneath them.
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.
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()
}
}