Working with Data

Learn how to provide data to your table using type-safe or array-based APIs.

Two Approaches

SwiftDataTables supports two approaches for providing data:

  1. Type-safe API - Define columns using KeyPaths on your model types. Requires Identifiable conformance, enabling animated updates when data changes.
  2. Array-based API - Pass a 2D array of strings for simple static displays. No model required.

Choose the type-safe API when you have model types and want diffing/animations. Choose the array-based API for quick prototyping or truly static data.

Static Displays

For simple tables where data doesn't change, use the array-based initializer:

let data: [[String]] = [
    ["Alice", "Engineering", "Senior"],
    ["Bob", "Marketing", "Manager"],
    ["Carol", "Sales", "Associate"]
]

let headers = ["Name", "Department", "Role"]

let dataTable = SwiftDataTable(data: data, headerTitles: headers)

Type-Safe API with Models

The type-safe API works directly with your model types. The key requirement: models must conform to Identifiable.

struct Person: Identifiable {
    let id: Int        // Already have a unique ID? You're done.
    let name: String
    let email: String
    let department: String
}

let columns: [DataTableColumn<Person>] = [
    .init("Name", \.name),
    .init("Email", \.email),
    .init("Department", \.department)
]

let dataTable = SwiftDataTable(data: people, columns: columns)

The id property enables diffing. Without it, SwiftDataTables can't determine which rows changed.

Loading and Updating Data

// Initial load
let people = await fetchPeople()
dataTable.setData(people, animatingDifferences: true)

// Later, when data changes
let updatedPeople = await fetchPeople()
dataTable.setData(updatedPeople, animatingDifferences: true)  // Only changed rows animate

SwiftDataTables compares the old and new arrays by ID, then:

  • Animates insertions and deletions
  • Updates changed rows in place
  • Leaves unchanged rows alone
  • Preserves scroll position

Data Transformations

Formatting Values

let columns: [DataTableColumn<Product>] = [
    .init("Product", \.name),
    .init("Price") { String(format: "$%.2f", $0.price) },
    .init("In Stock") { $0.inStock ? "Yes" : "No" }
]

Date Formatting

let dateFormatter: DateFormatter = {
    let f = DateFormatter()
    f.dateStyle = .medium
    return f
}()

let columns: [DataTableColumn<Event>] = [
    .init("Event", \.name),
    .init("Date") { dateFormatter.string(from: $0.date) }
]

CSV and JSON Data

When the schema isn't known at compile time, create a simple wrapper struct:

struct CSVRow: Identifiable {
    let id: Int          // Row index as ID
    let values: [String] // Column values
}

func loadCSV(from url: URL) {
    let csvData = parseCSV(url)
    let headers = csvData.headers

    // Create columns dynamically
    let columns: [DataTableColumn<CSVRow>] = headers.enumerated().map { index, header in
        .init(header) { row in
            row.values[index]
        }
    }

    // Create rows with index as ID
    let rows = csvData.rows.enumerated().map { index, values in
        CSVRow(id: index, values: values)
    }

    dataTable = SwiftDataTable(data: rows, columns: columns)
}