Skip to main content
DataTable renders data in either a scrollable grid (the default) or a list view optimised for portrait iPhone. It accepts a TableModel as its single data source.

Data model overview

The data model is composed of two main types:
TypeRole
TableModelObservable data source: header row, data rows, layout options
TableRowItemA single row: an array of DataItem cells plus optional leading/trailing accessories

DataItem types

TypeUse for
DataTextItemPlain or styled string values
DataImageItemAn Image value
DataDateItemA Date formatted by the column’s ColumnAttribute
DataTimeItemA time-only Date
DataDurationItemA TimeInterval formatted as a duration
DataListItemA string selected from a predefined list (supports inline editing)

Creating a DataTable

1

Define the header row

let headerTitles: [DataItem] = [
    DataTextItem("Order"),
    DataTextItem("Customer"),
    DataTextItem("Amount"),
    DataTextItem("Status")
]
let header = TableRowItem(data: headerTitles)
2

Build the data rows

let rows: [TableRowItem] = orders.map { order in
    TableRowItem(data: [
        DataTextItem(order.id),
        DataTextItem(order.customerName),
        DataTextItem(order.formattedAmount),
        DataTextItem(order.status, order.status == "Overdue" ? .red : nil)
    ])
}
3

Create and configure the model

let model = TableModel(
    headerData: header,
    rowData: rows,
    isHeaderSticky: true,
    isFirstColumnSticky: true,
    showListView: false
)

// Optional: respond to row taps
model.didSelectRowAt = { rowIndex in
    print("Tapped row \(rowIndex)")
}

// Optional: pre-select rows
model.selectedIndexes = [0, 2]
4

Embed DataTable in your view

struct OrdersView: View {
    let model: TableModel

    var body: some View {
        DataTable(model: model)
            .frame(height: 400)
    }
}

Key TableModel parameters

headerData
TableRowItem?
The header row. When nil, no header is rendered.
rowData
[TableRowItem]
The array of data rows.
isHeaderSticky
Bool
default:"false"
Keeps the header visible when the user scrolls vertically.
isFirstColumnSticky
Bool
default:"false"
Keeps the first column visible when the user scrolls horizontally.
showListView
Bool
default:"false"
Switches to list layout (suited to iPhone portrait). In list mode each row maps to an ObjectItem using automatic field binding.
editMode
TableModel.EditMode
default:".none"
.none — display only. .select — row selection checkboxes. .inline — individual cell editing.
isPinchZoomEnable
Bool
default:"false"
Enables two-finger zoom on the grid.
columnAttributes
[ColumnAttribute]
Per-column width, alignment, and formatting attributes.

DataTable view modifiers

DataTable(model: model)
    .headerSticky(true)              // sticky header
    .firstColumnSticky(true)         // sticky first column
    .pinchZoomEnable(true)           // pinch to zoom
    .showListView(false)             // grid vs list
    .showColumnDivider(true)         // column lines
    .showRowDivider(true)            // row lines
    .minRowHeight(56)                // minimum row height
    .minColumnWidth(80)              // minimum column width
    .backgroundColor(Color.clear)   // background

Inline editing

To enable inline editing, set editMode to .inline and supply a validateDataItem closure:
model.editMode = .inline

model.validateDataItem = { rowIndex, columnIndex, dataItem in
    guard let textItem = dataItem as? DataTextItem else {
        return (true, nil)
    }
    if textItem.text.isEmpty {
        return (false, "Value cannot be empty.")
    }
    return (true, nil)
}

model.valueDidChange = { change in
    print("Row \(change.rowIndex), column \(change.columnIndex): \(change.text)")
}
Call model.onSave(true) to commit changes or model.onSave(false) to roll them back.

List view mode and automatic ObjectItem binding

When showListView is true, DataTable maps each TableRowItem to an ObjectItem using positional field binding:
Column indexObjectItem field
0 (text)title
1 (text)subtitle
2 (text)footnote
3 (text)status
4 (text)substatus
0 (image)detailImage
let model = TableModel(
    headerData: header,
    rowData: rows,
    showListView: true
)
DataTable(model: model)
Set DataTable.showListView(true) via the view modifier or the TableModel property. In list view mode, horizontal scrolling is disabled and the rows render as standard Fiori list items.

Build docs developers (and LLMs) love