Skip to content

Commit 01573bf

Browse files
pavankatariaclaude
andcommitted
Deprecate array-based initializer in favor of typed API
- Mark init(data:headerTitles:) as deprecated - Mark init(data:[[String]], headerTitles:) as deprecated - Update WorkingWithData.md with dynamic data examples (CSV, JSON, queries) - Update MigratingTo09.md with array-based API migration guidance The typed API with DataTableColumn<T> is now the recommended approach for all use cases, including dynamic data sources. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 3d51993 commit 01573bf

File tree

3 files changed

+175
-72
lines changed

3 files changed

+175
-72
lines changed

SwiftDataTables/Classes/SwiftDataTable.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,25 @@ public class SwiftDataTable: UIView {
254254
self.registerObservers()
255255
}
256256

257+
/// Creates a data table with raw array data and header titles.
258+
///
259+
/// - Important: This initializer is deprecated. Use the type-safe column API instead:
260+
/// ```swift
261+
/// let columns: [DataTableColumn<YourModel>] = [
262+
/// .init("Name", \.name),
263+
/// .init("Age", \.age)
264+
/// ]
265+
/// let table = SwiftDataTable(columns: columns)
266+
/// table.setData(yourModels, animatingDifferences: true)
267+
/// ```
268+
///
269+
/// The type-safe API provides:
270+
/// - Automatic diffing with animated updates
271+
/// - Type safety via generics and KeyPaths
272+
/// - No manual array conversion
273+
///
274+
/// For dynamic data (CSV, JSON, database queries), see <doc:WorkingWithData>.
275+
@available(*, deprecated, message: "Use init(columns:) with DataTableColumn<T> for type-safe diffing. See documentation for migrating dynamic data.")
257276
public init(data: DataTableContent,
258277
headerTitles: [String],
259278
options: DataTableConfiguration = DataTableConfiguration(),
@@ -266,6 +285,11 @@ public class SwiftDataTable: UIView {
266285

267286

268287
}
288+
/// Creates a data table with string array data and header titles.
289+
///
290+
/// - Important: This initializer is deprecated. Use the type-safe column API instead.
291+
/// See ``init(data:headerTitles:options:frame:)-7kg3f`` for migration guidance.
292+
@available(*, deprecated, message: "Use init(columns:) with DataTableColumn<T> for type-safe diffing.")
269293
public convenience init(data: [[String]],
270294
headerTitles: [String],
271295
options: DataTableConfiguration = DataTableConfiguration(),

SwiftDataTables/SwiftDataTables.docc/MigratingTo09.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,40 @@ The following are deprecated and will be removed in a future version:
2424

2525
| Deprecated | Replacement | Reason |
2626
|------------|-------------|--------|
27+
| `init(data:headerTitles:)` | `init(columns:)` | No diffing support, manual array conversion |
2728
| `SwiftDataTableDataSource` protocol | Direct data pattern | Boilerplate-heavy, no diffing |
2829
| `reload()` method | `setData(_:animatingDifferences:)` | Resets scroll, no animations |
2930
| `.largeScale()` | `.automatic(estimated:prefetchWindow:)` | Renamed for clarity |
3031

32+
### Array-Based Initializer
33+
34+
The array-based initializer is deprecated because:
35+
- No automatic diffing (can't track which rows changed)
36+
- Manual conversion required (model → array)
37+
- No type safety (easy to mix up column order)
38+
39+
**Before (deprecated):**
40+
41+
```swift
42+
let data: [[DataTableValueType]] = items.map { item in
43+
[.string(item.name), .int(item.age)]
44+
}
45+
let table = SwiftDataTable(data: data, headerTitles: ["Name", "Age"])
46+
```
47+
48+
**After (recommended):**
49+
50+
```swift
51+
let columns: [DataTableColumn<Item>] = [
52+
.init("Name", \.name),
53+
.init("Age", \.age)
54+
]
55+
let table = SwiftDataTable(columns: columns)
56+
table.setData(items, animatingDifferences: true)
57+
```
58+
59+
For dynamic data (CSV, JSON, database queries), see <doc:WorkingWithData>.
60+
3161
## Migration Path
3262

3363
### Step 1: Remove DataSource Protocol
Lines changed: 121 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,168 @@
11
# Working with Data
22

3-
Learn the different ways to provide data to your table.
3+
Learn how to provide data to your table using the type-safe API.
44

55
## Overview
66

7-
SwiftDataTables supports multiple data formats, from simple string arrays to type-safe model objects. Choose the approach that best fits your needs.
7+
SwiftDataTables uses a type-safe API with `DataTableColumn<T>` to define your table structure. This approach provides automatic diffing, animated updates, and compile-time safety.
88

9-
## Data Formats
9+
## Basic Usage
1010

11-
### String Arrays (Simplest)
12-
13-
For quick prototypes or simple data:
11+
Define columns using KeyPaths, then pass your model array:
1412

1513
```swift
16-
let data = [
17-
["Alice", "28", "London"],
18-
["Bob", "34", "Paris"],
19-
["Carol", "25", "Berlin"]
14+
struct Person: Identifiable {
15+
let id: Int
16+
let name: String
17+
let age: Int
18+
let city: String
19+
}
20+
21+
let columns: [DataTableColumn<Person>] = [
22+
.init("Name", \.name),
23+
.init("Age", \.age),
24+
.init("City", \.city)
2025
]
2126

22-
let dataTable = SwiftDataTable(
23-
data: data,
24-
headerTitles: ["Name", "Age", "City"]
25-
)
27+
let dataTable = SwiftDataTable(columns: columns)
28+
29+
// Load data later
30+
dataTable.setData(people, animatingDifferences: true)
2631
```
2732

28-
### DataTableValueType Arrays (Typed Values)
33+
## Dynamic Data Sources
2934

30-
For explicit control over sorting behavior:
35+
For data without a predefined model (CSV files, JSON responses, database queries), create a wrapper struct:
36+
37+
### CSV Import
3138

3239
```swift
33-
let data: [[DataTableValueType]] = [
34-
[.string("Alice"), .int(28), .string("London")],
35-
[.string("Bob"), .int(34), .string("Paris")],
36-
[.string("Carol"), .int(25), .string("Berlin")]
37-
]
40+
/// Wrapper for CSV row data
41+
struct CSVRow: Identifiable {
42+
let id: Int // Row index as ID
43+
let values: [String] // Column values
44+
}
3845

39-
let dataTable = SwiftDataTable(
40-
data: data,
41-
headerTitles: ["Name", "Age", "City"]
42-
)
43-
```
46+
func loadCSV(from url: URL) {
47+
let csvData = parseCSV(url) // Your CSV parser
48+
let headers = csvData.headers
4449

45-
Using `.int()` instead of `.string("28")` ensures numeric sorting (2, 10, 25) instead of alphabetic ("10", "2", "25").
50+
// Create columns dynamically
51+
let columns: [DataTableColumn<CSVRow>] = headers.enumerated().map { index, header in
52+
.init(header) { row in
53+
.string(row.values[index])
54+
}
55+
}
4656

47-
### Model Objects (Recommended)
57+
// Create rows with index as ID
58+
let rows = csvData.rows.enumerated().map { index, values in
59+
CSVRow(id: index, values: values)
60+
}
61+
62+
dataTable = SwiftDataTable(columns: columns)
63+
dataTable.setData(rows, animatingDifferences: false)
64+
}
65+
```
4866

49-
For production apps, use your own model types:
67+
### JSON Response
5068

5169
```swift
52-
struct Person: Identifiable {
53-
let id: Int
54-
let name: String
55-
let age: Int
56-
let city: String
70+
/// Wrapper for dynamic JSON objects
71+
struct JSONRow: Identifiable {
72+
let id: String
73+
let data: [String: Any]
74+
75+
func value(for key: String) -> DataTableValueType {
76+
switch data[key] {
77+
case let string as String: return .string(string)
78+
case let int as Int: return .int(int)
79+
case let double as Double: return .double(double)
80+
default: return .string("")
81+
}
82+
}
5783
}
5884

59-
let people = [
60-
Person(id: 1, name: "Alice", age: 28, city: "London"),
61-
Person(id: 2, name: "Bob", age: 34, city: "Paris"),
62-
Person(id: 3, name: "Carol", age: 25, city: "Berlin")
63-
]
85+
func loadJSON(_ json: [[String: Any]], keys: [String]) {
86+
let columns: [DataTableColumn<JSONRow>] = keys.map { key in
87+
.init(key.capitalized) { row in
88+
row.value(for: key)
89+
}
90+
}
6491

65-
let columns: [DataTableColumn<Person>] = [
66-
.init("Name", \.name),
67-
.init("Age", \.age),
68-
.init("City", \.city)
69-
]
92+
let rows = json.enumerated().map { index, dict in
93+
let id = (dict["id"] as? String) ?? "\(index)"
94+
return JSONRow(id: id, data: dict)
95+
}
7096

71-
let dataTable = SwiftDataTable(data: people, columns: columns)
97+
dataTable = SwiftDataTable(columns: columns)
98+
dataTable.setData(rows, animatingDifferences: false)
99+
}
72100
```
73101

74-
## Accessing Data
75-
76-
### Get All Data
102+
### Database Query
77103

78104
```swift
79-
// For typed tables
80-
let allPeople: [Person] = dataTable.allModels()
105+
/// Wrapper for database result rows
106+
struct QueryRow: Identifiable {
107+
let id: Int64 // Primary key or row number
108+
let columns: [String: DataTableValueType]
109+
}
110+
111+
func executeQuery(_ sql: String, columnNames: [String]) async {
112+
let results = await database.query(sql)
113+
114+
let columns: [DataTableColumn<QueryRow>] = columnNames.map { name in
115+
.init(name) { row in
116+
row.columns[name] ?? .string("")
117+
}
118+
}
119+
120+
let rows = results.enumerated().map { index, record in
121+
QueryRow(
122+
id: record.primaryKey ?? Int64(index),
123+
columns: record.toDictionary()
124+
)
125+
}
81126

82-
// For array-based tables
83-
let rowCount = dataTable.currentRowCount
127+
dataTable = SwiftDataTable(columns: columns)
128+
dataTable.setData(rows, animatingDifferences: false)
129+
}
84130
```
85131

86-
### Get Specific Row
132+
## Why Use a Wrapper?
133+
134+
The typed API requires `Identifiable` conformance for diffing. Wrapping dynamic data provides:
135+
136+
| Benefit | Description |
137+
|---------|-------------|
138+
| **Diffing** | Rows can be tracked by ID for animated updates |
139+
| **Type Safety** | Compiler ensures column extractors match the row type |
140+
| **Performance** | Only changed rows update, not the entire table |
141+
| **Consistency** | Same API whether data is static or dynamic |
142+
143+
## Accessing Data
144+
145+
### Get All Models
87146

88147
```swift
89-
// For typed tables
90-
if let person: Person = dataTable.model(at: 5) {
91-
print("Row 5 is \(person.name)")
148+
if let allPeople: [Person] = dataTable.allModels() {
149+
print("Total: \(allPeople.count)")
92150
}
93-
94-
// For array-based tables
95-
let rowData = dataTable.data(for: 5) // Returns [DataTableValueType]
96151
```
97152

98-
### Get Filtered Data
99-
100-
After search/filter, access visible rows:
153+
### Get Specific Row
101154

102155
```swift
103-
let visibleCount = dataTable.currentRowCount // After filtering
156+
if let person: Person = dataTable.model(at: 5) {
157+
print("Row 5: \(person.name)")
158+
}
104159
```
105160

106161
## Data Transformations
107162

108163
### Formatting Values
109164

110-
Use closures in column definitions:
165+
Use closures for formatted display:
111166

112167
```swift
113168
let columns: [DataTableColumn<Product>] = [
@@ -138,12 +193,6 @@ let columns: [DataTableColumn<Order>] = [
138193
### Date Formatting
139194

140195
```swift
141-
struct Event: Identifiable {
142-
let id: Int
143-
let name: String
144-
let date: Date
145-
}
146-
147196
let dateFormatter: DateFormatter = {
148197
let f = DateFormatter()
149198
f.dateStyle = .medium
@@ -165,7 +214,7 @@ Handle empty data gracefully:
165214
let dataTable = SwiftDataTable(columns: columns)
166215

167216
// Later, populate with animation
168-
let items = fetchItems()
217+
let items = await fetchItems()
169218
dataTable.setData(items, animatingDifferences: true)
170219
```
171220

@@ -189,4 +238,4 @@ func setTableData(_ rawItems: [RawItem]) {
189238

190239
- <doc:TypeSafeColumns>
191240
- <doc:AnimatedUpdates>
192-
- ``DataTableValueType``
241+
- ``DataTableColumn``

0 commit comments

Comments
 (0)