How it works
TableRowContent is the protocol that describes the rows of a Table, just as View describes the contents of a view hierarchy. Every value you place inside a table's row builder — whether you write rows explicitly with TableRow or let the table generate them from a collection — conforms to TableRowContent, which ties each row to the data type it presents through its associated TableRowValue. You rarely name the protocol directly; instead you reach for it conceptually whenever you need to reason about what a table's body may contain, or when you build a reusable component that returns table rows from a @TableRowBuilder closure.
Conform your row's value to Identifiable
TableRowContent is parameterized by an associated TableRowValue, which must be Identifiable so the table can track, diff, and select individual rows. In the example,
Persondeclareslet id = UUID()to satisfyIdentifiable, making it eligible to act as the row value carried by the table's content.Generate row content from a collection
When you initialize a Table with a sequence of identifiable values, SwiftUI synthesizes the TableRowContent for you — one row per element, keyed by each element's id. Here
Table(people)produces that row content automatically from thepeoplearray, so you never write a row literal even though each entry is backed by TableRowContent under the hood.Project values onto columns with key paths
A table's columns read fields out of the row value, and the column's value key path is what binds a TableColumn to a property of the TableRowValue. The
TableColumn("Name", value: \.name)andTableColumn("Role", value: \.role)lines pullnameandrolefrom eachPersonrow, which is how the row content surfaces as cells.Compose rows in a @TableRowBuilder closure
The trailing closure of Table is a @TableRowBuilder, a result builder whose result type is TableRowContent — the row-level analogue of @ViewBuilder. In the example the closure that holds the two
TableColumndeclarations is where row content is assembled, and any custom type returningsome TableRowContentcan be dropped into that same position.
Person(name: "Linus", role: "Hacker") to the people array and watch the table emit a new row with no other changes, since the row content is derived directly from the collection.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 TableRowContentDemo: View {
struct Person: Identifiable {
let id = UUID()
let name: String
let role: String
}
let people = [
Person(name: "Ada", role: "Engineer"),
Person(name: "Grace", role: "Admiral"),
Person(name: "Alan", role: "Theorist")
]
var body: some View {
Table(people) {
TableColumn("Name", value: \.name)
TableColumn("Role", value: \.role)
}
.padding()
}
}