How it works
TableColumnBuilder is the result builder that assembles the columns you list inside a Table. Whenever you write a sequence of TableColumn declarations in a table's content closure, the compiler hands them to TableColumnBuilder, which gathers them into the single column collection the table renders left to right. You rarely name this type directly — it is the @resultBuilder attached to the column closure parameter — but it is what lets you describe a table's structure declaratively, one column per statement, instead of constructing an array by hand. Reach for an understanding of it whenever you need to know which column expressions are valid in that closure and how they combine.
List columns in the table's content closure
The closure you pass to
Tableis annotated with@TableColumnBuilder, so each statement you write is treated as one column rather than ordinary imperative code. In the example, the twoTableColumnlines insideTable(people) { ... }are collected by the builder and become the table'sNameandRolecolumns in the order written.Supply each column as a TableColumn value
TableColumnBuilderacceptsTableColumnvalues as its building blocks; each one binds a column title to the content drawn for every row. HereTableColumn("Name")provides a view-building closure that receives apersonand returnsText(person.name).bold(), whileTableColumn("Role", value: \.role)uses the key-path form to display therolestring directly.Mix value-based and content-based columns freely
Because the builder only requires that each statement produce a column, you can combine the custom-content initializer and the key-path
value:initializer in the same closure. TheNamecolumn builds a styledTextper row, and theRolecolumn derives its cells straight from\.role— both flow throughTableColumnBuilderinto one column set.Rely on the row type tying the columns together
Every column in a builder closure must agree on the table's row element so the builder can type-check the collection as a whole. The
Table(people)overPersonvalues fixes that element type, which is why eachTableColumnhere can readperson.nameand the\.rolekey path againstPerson.
TableColumn("ID") { person in Text(person.id.uuidString) } beside the existing TableColumn declarations to see the builder absorb a new column into the same Table with no other changes.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 TableColumnBuilderDemo: View {
struct Person: Identifiable {
let id = UUID()
let name: String
let role: String
}
let people = [
Person(name: "Ada Lovelace", role: "Engineer"),
Person(name: "Alan Turing", role: "Scientist"),
Person(name: "Grace Hopper", role: "Admiral")
]
var body: some View {
Table(people) {
TableColumn("Name") { person in
Text(person.name).bold()
}
TableColumn("Role", value: \.role)
}
.padding()
}
}