|
1 | 1 | # SwiftUICharts |
2 | 2 |
|
3 | | -SwiftUICharts is an open-source chart library for SwiftUI with iOS 13 compatibility. |
| 3 | +SwiftUICharts is a composable, SwiftUI-native chart library for iOS 13+. |
4 | 4 |
|
5 | | -This release uses a fully composable, SwiftUI-idiomatic API based on immutable configuration and `ViewModifier` chains. |
6 | | - |
7 | | -## 2.0.0 Release |
8 | | - |
9 | | -Version `2.0.0` is the new major composable release. |
10 | | - |
11 | | -- New modifier-first API (`chartData`, `chartXRange`, `chartYRange`, `chartGridLines`, axis modifiers, line modifiers) |
12 | | -- Shared X-axis alignment model across chart types |
13 | | -- Optional interaction model (`chartInteractionValue`) and callback model (`chartSelectionHandler`) |
14 | | -- Streaming data support (`ChartStreamingDataSource`) |
15 | | -- Performance mode (`chartPerformance`) |
16 | | -- Accessibility + high-contrast presets |
17 | | -- Apple privacy manifest included for SDK distribution (`PrivacyInfo.xcprivacy`) |
| 5 | +`2.0.0` is a major release focused on immutable configuration, environment-driven composition, and modifier-based APIs. |
18 | 6 |
|
19 | 7 | <p align="center"> |
20 | 8 | <img src="Resources/linevid2.gif" width="30%"/> <img src="Resources/barvid2.gif" width="30%"/> <img src="Resources/pievid2.gif" width="30%"/> |
21 | 9 | </p> |
22 | 10 |
|
23 | | -## Charts |
24 | | - |
25 | | -- `LineChart` |
26 | | -- `BarChart` |
27 | | -- `PieChart` |
28 | | -- `RingsChart` |
| 11 | +## AI Agent Quick Context |
29 | 12 |
|
30 | | -## Installation |
31 | | - |
32 | | -Use Swift Package Manager in Xcode and add: |
| 13 | +Use this section when an AI agent generates code against this package. |
33 | 14 |
|
34 | | -`https://github.com/AppPear/ChartView` |
| 15 | +### API contract |
35 | 16 |
|
36 | | -For `2.0.0`, depend on the `2.0.0` tag or from `2.0.0` up to next major. |
| 17 | +- Use only `2.x` composable APIs. |
| 18 | +- Do not use legacy `1.x` types or mutating chains. |
| 19 | +- Build charts with `ViewModifier` composition. |
| 20 | +- Keep data source semantics explicit: |
| 21 | + - `chartData([Double])` = categorical slots |
| 22 | + - `chartData([(Double, Double)])` = numeric/continuous domain |
37 | 23 |
|
38 | | -## Migration |
| 24 | +### Use these APIs |
39 | 25 |
|
40 | | -This is a major composable API release. |
41 | | - |
42 | | -- Previous chain APIs like `.data`, `.rangeX`, `.rangeY`, `.setAxisXLabels`, `.setNumberOfHorizontalLines`, and line-specific setters were replaced by typed chart modifiers. |
43 | | -- Full old-to-new mapping: [MIGRATION.md](./MIGRATION.md) |
44 | | - |
45 | | -## Migration In 3 Steps |
46 | | - |
47 | | -1. Replace legacy view types (`LineChartView`, `BarChartView`, `PieChartView`, `MultiLineChartView`) with composable chart views. |
48 | | -2. Replace old chain methods with new modifiers. |
49 | | -3. Move interaction to container-level wiring: |
50 | | - - shared state: `.chartInteractionValue(ChartValue())` |
51 | | - - callback-driven: `.chartSelectionHandler { event in ... }` |
52 | | - |
53 | | -### Common replacements |
54 | | - |
55 | | -| Old | New | |
| 26 | +| Task | API | |
56 | 27 | | --- | --- | |
57 | | -| `.data([Double])` | `.chartData([Double])` | |
58 | | -| `.rangeX(...)` | `.chartXRange(...)` | |
59 | | -| `.rangeY(...)` | `.chartYRange(...)` | |
60 | | -| `.setNumberOfHorizontalLines(h)` | `.chartGridLines(horizontal: h, vertical: ...)` | |
61 | | -| `.setAxisXLabels(...)` | `.chartXAxisLabels(...)` | |
62 | | -| `.showChartMarks(...)` | `.chartLineMarks(...)` | |
63 | | - |
64 | | -## Quick Start |
| 28 | +| Set data | `chartData(...)` | |
| 29 | +| Set ranges | `chartXRange(...)`, `chartYRange(...)` | |
| 30 | +| Style chart | `chartStyle(...)` | |
| 31 | +| Grid config | `chartGridLines`, `chartGridStroke`, `chartGridBaseline` | |
| 32 | +| Axis labels/ticks | `chartXAxisLabels`, `chartYAxisLabels`, `chartXAxisAutoTicks`, `chartYAxisAutoTicks` | |
| 33 | +| Line tuning | `chartLineWidth`, `chartLineStyle`, `chartLineMarks`, `chartLineAnimation` | |
| 34 | +| Shared interaction | `chartInteractionValue(...)` | |
| 35 | +| Callback interaction | `chartSelectionHandler { event in ... }` | |
| 36 | +| Streaming data | `ChartStreamingDataSource` + `chartData(stream)` | |
| 37 | +| Large datasets | `chartPerformance(...)` | |
| 38 | + |
| 39 | +### Avoid these legacy APIs |
| 40 | + |
| 41 | +- `LineChartView`, `BarChartView`, `PieChartView`, `MultiLineChartView` |
| 42 | +- `.data(...)`, `.rangeX(...)`, `.rangeY(...)` |
| 43 | +- `.setAxisXLabels(...)`, `.setNumberOfHorizontalLines(...)` |
| 44 | +- `.showChartMarks(...)` (old form) |
65 | 45 |
|
66 | | -**Simple line chart** |
| 46 | +## Installation |
67 | 47 |
|
68 | | -<p align="left"> |
69 | | -<img src="Resources/chartpic1.png" width="350px"/> |
70 | | -</p> |
| 48 | +Add with Swift Package Manager: |
71 | 49 |
|
72 | | -```swift |
73 | | -LineChart() |
74 | | - .chartData([3, 5, 4, 1, 0, 2, 4, 1, 0, 2, 8]) |
75 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.orange, .red))) |
76 | | -``` |
| 50 | +`https://github.com/AppPear/ChartView` |
77 | 51 |
|
78 | | -**Add background grid** |
| 52 | +For this release, use tag `2.0.0` (or `from: "2.0.0"` up to next major). |
79 | 53 |
|
80 | | -<p align="left"> |
81 | | -<img src="Resources/chartpic2.png" width="350px"/> |
82 | | -</p> |
| 54 | +## 30-Second Quick Start |
83 | 55 |
|
84 | 56 | ```swift |
85 | | -ChartGrid { |
86 | | - LineChart() |
87 | | - .chartData([3, 5, 4, 1, 0, 2, 4, 1, 0, 2, 8]) |
88 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.orange, .red))) |
| 57 | +import SwiftUI |
| 58 | +import SwiftUICharts |
| 59 | + |
| 60 | +struct DemoView: View { |
| 61 | + var body: some View { |
| 62 | + AxisLabels { |
| 63 | + ChartGrid { |
| 64 | + LineChart() |
| 65 | + .chartData([12, 34, 23, 18, 36, 22, 26]) |
| 66 | + .chartYRange(10...40) |
| 67 | + .chartLineMarks(true, color: ColorGradient(.blue, .purple)) |
| 68 | + .chartStyle( |
| 69 | + ChartStyle( |
| 70 | + backgroundColor: .white, |
| 71 | + foregroundColor: ColorGradient(.blue, .purple) |
| 72 | + ) |
| 73 | + ) |
| 74 | + } |
| 75 | + .chartGridLines(horizontal: 5, vertical: 6) |
| 76 | + } |
| 77 | + .chartXAxisLabels(["M", "T", "W", "T", "F", "S", "S"]) |
| 78 | + .chartAxisColor(.secondary) |
| 79 | + .chartAxisFont(.caption) |
| 80 | + .frame(height: 220) |
| 81 | + .padding() |
| 82 | + } |
89 | 83 | } |
90 | | -.chartGridLines(horizontal: 5, vertical: 4) |
91 | 84 | ``` |
92 | 85 |
|
93 | | -**Axis labels** |
| 86 | +## Feature Recipes |
94 | 87 |
|
95 | | -<p align="left"> |
96 | | -<img src="Resources/chartpic3.png" width="350px"/> |
97 | | -</p> |
| 88 | +### 1) Mixed bar + line |
98 | 89 |
|
99 | 90 | ```swift |
100 | 91 | AxisLabels { |
101 | 92 | ChartGrid { |
| 93 | + BarChart() |
| 94 | + .chartData([2, 4, 1, 3, 5]) |
| 95 | + .chartStyle(ChartStyle(backgroundColor: .white, |
| 96 | + foregroundColor: ColorGradient(.orange, .red))) |
| 97 | + |
102 | 98 | LineChart() |
103 | | - .chartData([3, 5, 4, 1, 0, 2, 4, 1, 0, 2, 8]) |
104 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.orange, .red))) |
| 99 | + .chartData([2, 4, 1, 3, 5]) |
| 100 | + .chartLineMarks(true) |
| 101 | + .chartStyle(ChartStyle(backgroundColor: .white, |
| 102 | + foregroundColor: ColorGradient(.blue, .purple))) |
105 | 103 | } |
106 | | - .chartGridLines(horizontal: 5, vertical: 4) |
| 104 | + .chartGridLines(horizontal: 5, vertical: 5) |
107 | 105 | } |
108 | | -.chartXAxisLabels([(1, "Nov"), (2, "Dec"), (3, "Jan")], range: 1...3) |
| 106 | +.chartXAxisLabels([(0, "A"), (1, "B"), (2, "C"), (3, "D"), (4, "E")], range: 0...4) |
109 | 107 | ``` |
110 | 108 |
|
111 | | -**Line config + ranges** |
112 | | - |
113 | | -<p align="left"> |
114 | | -<img src="Resources/chartpic5.png" width="350px"/> |
115 | | -</p> |
| 109 | +### 2) Shared interaction state |
116 | 110 |
|
117 | 111 | ```swift |
118 | | -AxisLabels { |
119 | | - ChartGrid { |
120 | | - LineChart() |
121 | | - .chartLineMarks(true) |
122 | | - .chartData([3, 5, 4, 1, 0, 2, 4, 1, 0, 2, 8]) |
123 | | - .chartYRange(0...10) |
124 | | - .chartXRange(0...5) |
125 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.orange, .red))) |
126 | | - } |
127 | | - .chartGridLines(horizontal: 5, vertical: 4) |
| 112 | +let selected = ChartValue() |
| 113 | + |
| 114 | +VStack(alignment: .leading) { |
| 115 | + ChartLabel("Weekly Sales", type: .title) |
| 116 | + ChartLabel("Drag bars", type: .legend, format: "%.1f") |
| 117 | + |
| 118 | + BarChart().chartData([14, 22, 18, 31, 26, 19, 24]) |
128 | 119 | } |
129 | | -.chartXAxisLabels([(1, "Nov"), (2, "Dec"), (3, "Jan")], range: 1...3) |
| 120 | +.chartInteractionValue(selected) |
130 | 121 | ``` |
131 | 122 |
|
132 | | -**Mix chart types** |
| 123 | +### 3) Callback-based interaction |
133 | 124 |
|
134 | | -<p align="left"> |
135 | | -<img src="Resources/chartpic7.png" width="350px"/> |
136 | | -</p> |
| 125 | +```swift |
| 126 | +BarChart() |
| 127 | + .chartData([8, 11, 13, 9, 12]) |
| 128 | + .chartSelectionHandler { event in |
| 129 | + guard event.isActive, |
| 130 | + let value = event.value, |
| 131 | + let index = event.index else { return } |
| 132 | + print("selected", index, value) |
| 133 | + } |
| 134 | +``` |
| 135 | + |
| 136 | +### 4) Dynamic streaming data |
137 | 137 |
|
138 | 138 | ```swift |
139 | | -AxisLabels { |
140 | | - ChartGrid { |
141 | | - BarChart() |
142 | | - .chartData([2, 4, 1, 3]) |
143 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.orange, .red))) |
| 139 | +@ObservedObject private var stream = ChartStreamingDataSource( |
| 140 | + initialValues: [18, 23, 20, 27, 29, 24], |
| 141 | + windowSize: 6, |
| 142 | + autoScroll: true |
| 143 | +) |
144 | 144 |
|
145 | | - LineChart() |
146 | | - .chartLineMarks(true) |
147 | | - .chartData([2, 4, 1, 3]) |
148 | | - .chartStyle(ChartStyle(backgroundColor: .white, foregroundColor: ColorGradient(.blue, .purple))) |
149 | | - } |
150 | | - .chartGridLines(horizontal: 5, vertical: 4) |
151 | | -} |
152 | | -.chartXAxisLabels([(1, "Nov"), (2, "Dec"), (3, "Jan")], range: 1...3) |
| 145 | +LineChart() |
| 146 | + .chartData(stream) |
| 147 | + .chartYRange(stream.suggestedYRange) |
| 148 | +``` |
| 149 | + |
| 150 | +### 5) Performance mode for large datasets |
| 151 | + |
| 152 | +```swift |
| 153 | +LineChart() |
| 154 | + .chartData(largeSeries) |
| 155 | + .chartPerformance(.automatic(threshold: 600, |
| 156 | + maxPoints: 180, |
| 157 | + simplifyLineStyle: true)) |
153 | 158 | ``` |
154 | 159 |
|
155 | | -## Full Examples |
| 160 | +## Migration |
| 161 | + |
| 162 | +This is a major breaking release. |
| 163 | + |
| 164 | +- Full migration guide: [MIGRATION.md](./MIGRATION.md) |
| 165 | +- Quick examples: [example.md](./example.md) |
| 166 | + |
| 167 | +## Documentation Map |
| 168 | + |
| 169 | +- Wiki index: [docs/wiki/README.md](./docs/wiki/README.md) |
| 170 | +- Getting started: [docs/wiki/01-getting-started.md](./docs/wiki/01-getting-started.md) |
| 171 | +- Modifiers: [docs/wiki/02-composable-modifiers.md](./docs/wiki/02-composable-modifiers.md) |
| 172 | +- Interaction: [docs/wiki/03-interaction-and-selection.md](./docs/wiki/03-interaction-and-selection.md) |
| 173 | +- Streaming: [docs/wiki/04-dynamic-and-streaming-data.md](./docs/wiki/04-dynamic-and-streaming-data.md) |
| 174 | +- Performance: [docs/wiki/05-performance-and-large-datasets.md](./docs/wiki/05-performance-and-large-datasets.md) |
| 175 | +- Axis alignment: [docs/wiki/06-unified-axis-and-label-alignment.md](./docs/wiki/06-unified-axis-and-label-alignment.md) |
| 176 | +- Migration summary: [docs/wiki/07-migration-from-1x.md](./docs/wiki/07-migration-from-1x.md) |
| 177 | + |
| 178 | +## Example App |
| 179 | + |
| 180 | +The showcase app demonstrates all major features: |
156 | 181 |
|
157 | | -See [example.md](./example.md) and [`Examples/SwiftUIChartsShowcase`](./Examples/SwiftUIChartsShowcase) for complete showcase code. |
| 182 | +`Examples/SwiftUIChartsShowcase` |
158 | 183 |
|
159 | 184 | ## Release Notes |
160 | 185 |
|
161 | 186 | - Changelog: [CHANGELOG.md](./CHANGELOG.md) |
162 | | -- Migration details: [MIGRATION.md](./MIGRATION.md) |
| 187 | +- Includes Apple privacy manifest: `Sources/SwiftUICharts/PrivacyInfo.xcprivacy` |
0 commit comments