TechnologiesSwiftUILists and Collections

DynamicTableRowContent protocol

iOSmacOStvOSwatchOSvisionOS✓ renders

A type of table row content that generates table rows from an underlying

How it works

DynamicTableRowContent is the protocol that table row builders adopt when their rows aren't fixed at compile time but are produced from a collection of data. Where a static TableRow stands in for a single known element, conformers to this protocol describe a whole family of rows generated one-per-element, keeping each row tied to its underlying identified value. Reach for it whenever a Table needs to grow, shrink, or reorder its rows in step with a data source rather than enumerating them by hand. In practice you rarely name the protocol directly — you obtain a conforming value by iterating your data inside the table's row builder.

  1. Generate rows from data with ForEach

    The canonical source of dynamic row content is ForEach, whose result conforms to DynamicTableRowContent. Placed in the row builder, it visits each element of a collection and emits one row apiece, so the table's contents track the array. Here ForEach(fruits) walks the three Fruit values and produces a row for each.

  2. Emit one TableRow per element

    Inside the dynamic builder you return a TableRow for the current element, binding that row to a specific value. The row carries the data that each TableColumn then reads when it renders a cell. In the example TableRow($0) wraps the per-iteration Fruit, becoming the unit of dynamic content that DynamicTableRowContent represents.

  3. Identify each row so updates stay stable

    Dynamic row content relies on stable identity to diff, animate, and select rows as the data changes. Conforming your model to Identifiable supplies that identity automatically. Fruit declares let id = UUID(), which lets ForEach(fruits) and its TableRow values match rows to elements across reloads without an explicit id key path.

  4. Place the content in the table's rows builder

    DynamicTableRowContent slots into the trailing rows: closure of a Table built from a row type, while the columns closure stays separate. The Table(of: Fruit.self) form pairs the TableColumn definitions with the dynamic rows, so ForEach(fruits) { TableRow($0) } is the value satisfying the row-content requirement and $0.name, $0.count are resolved column-by-column.

Try it — Add another Fruit(name:count:) entry to the fruits array and watch a new row appear without touching the Table or TableColumn code — the dynamic row content regenerates from the data.

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.

DynamicTableRowContent.swift
struct DynamicTableRowContentDemo: View {
    struct Fruit: Identifiable {
        let id = UUID()
        let name: String
        let count: Int
    }
    let fruits = [
        Fruit(name: "Apple", count: 12),
        Fruit(name: "Banana", count: 7),
        Fruit(name: "Cherry", count: 30)
    ]
    var body: some View {
        Table(of: Fruit.self) {
            TableColumn("Name") { Text($0.name) }
            TableColumn("Count") { Text("\($0.count)") }
        } rows: {
            ForEach(fruits) { TableRow($0) }
        }
        .padding()
    }
}
Live preview
Name Count
Name Count
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →