Swift Charts Accessibility, Fallback, and Resources
Table of Contents
- Accessibility
- Meaningful Labels
- Custom Audio Graphs
- Composite Example
- Fallback Strategies
- Version Breakdown
- WWDC Sessions
- Summary Checklist
Accessibility
Swift Charts provides built-in accessibility support. VoiceOver users get three rotor actions automatically:
- Describe Chart — overview of axes and data series
- Audio Graph — sonification where pitch represents data values
- Chart Detail — interactive mode for exploring individual data points
Meaningful Labels
Always use clear, descriptive strings in .value(_, _) calls. These labels are read by VoiceOver and used in the Audio Graph.
// Good — descriptive labels
LineMark(
x: .value("Date", entry.date),
y: .value("Daily Steps", entry.count)
)
// Bad — generic labels
LineMark(
x: .value("X", entry.date),
y: .value("Y", entry.count)
)Custom Audio Graphs
For advanced accessibility, conform your chart view to AXChartDescriptorRepresentable and implement makeChartDescriptor(). Attach it with .accessibilityChartDescriptor(self).
struct StepsChart: View, AXChartDescriptorRepresentable {
let steps: [DailySteps]
var body: some View {
Chart(steps) { day in
LineMark(x: .value("Date", day.date), y: .value("Steps", day.count))
}
.accessibilityChartDescriptor(self)
}
func makeChartDescriptor() -> AXChartDescriptor {
guard let first = steps.first, let last = steps.last else {
return AXChartDescriptor(title: "Daily Step Count", summary: nil,
xAxis: AXNumericDataAxisDescriptor(title: "Date", range: 0...1, gridlinePositions: []) { "\($0)" },
yAxis: AXNumericDataAxisDescriptor(title: "Steps", range: 0...1, gridlinePositions: []) { "\($0)" },
additionalAxes: [], series: [])
}
let xAxis = AXDateDataAxisDescriptor(
title: "Date", range: first.date...last.date, gridlinePositions: [])
let yAxis = AXNumericDataAxisDescriptor(
title: "Steps", range: 0...Double(steps.map(\.count).max() ?? 0),
gridlinePositions: []) { "\(Int($0)) steps" }
let series = AXDataSeriesDescriptor(
name: "Daily Steps", isContinuous: true,
dataPoints: steps.map { .init(x: $0.date, y: Double($0.count)) })
return AXChartDescriptor(title: "Daily Step Count", summary: nil,
xAxis: xAxis, yAxis: yAxis, additionalAxes: [], series: [series])
}
}Composite Example
A scrollable bar chart with range selection combining multiple iOS 17+ APIs:
@State private var selectedRange: ClosedRange<Int>?
Chart(weeklyRevenue) { week in
BarMark(x: .value("Week", week.index), y: .value("Revenue", week.revenue))
.foregroundStyle(by: .value("Region", week.region))
}
.chartScrollableAxes(.horizontal)
.chartXVisibleDomain(length: 8)
.chartXSelection(range: $selectedRange)
.chartXAxis {
AxisMarks(values: .stride(by: 1)) {
AxisGridLine()
AxisValueLabel { Text("W\($0.as(Int.self) ?? 0)") }
}
}Fallback Strategies
Gate advanced APIs with #available and provide a fallback chart without the gated features. Because chart modifiers like .chartXSelection change the return type, you must duplicate the entire Chart — you cannot conditionally apply the modifier:
Version Breakdown
- iOS 16+:
Chart, custom axes, scales,BarMark,LineMark,AreaMark,PointMark,RectangleMark,RuleMark,ChartProxy,chartOverlay,chartBackground - iOS 17+:
SectorMark,chartXSelection,chartYSelection,chartAngleSelection,chartScrollableAxes, visible-domain scrolling APIs,chartGesture - iOS 18+:
AreaPlot,BarPlot,LinePlot,PointPlot,RectanglePlot,RulePlot,SectorPlot, function plotting - iOS 26+:
Chart3D,SurfacePlot, Z-axis marks, 3D camera and pose APIs
WWDC Sessions
- Hello Swift Charts (WWDC 2022) — introduction to the framework
- Swift Charts: Raise the bar (WWDC 2022) — marks, composition, customization
- Design an effective chart (WWDC 2022) — chart design principles
- Design app experiences with charts (WWDC 2022) — integrating charts into app UX
- Explore pie charts and interactivity in Swift Charts (WWDC 2023) — SectorMark, selection, scrolling
- Swift Charts: Vectorized and function plots (WWDC 2024) — LinePlot, AreaPlot, function plotting
- Bring Swift Charts to the third dimension (WWDC 2025) — Chart3D, SurfacePlot, 3D marks
Summary Checklist
- [ ]
import Chartsis present in files using chart types - [ ] Deployment target matches the APIs used (
Charton iOS 16+, selection andSectorMarkon iOS 17+, plot types on iOS 18+,Chart3Don iOS 26+) - [ ] Chart data models use
Identifiable(orChart(data, id:)is provided) - [ ] All chart families are represented with the correct mark type
- [ ] Axes use
AxisMarkswhen default ticks are too dense or unclear - [ ]
chartXScaleorchartYScaleis set when fixed domains matter - [ ] Chart-wide modifiers are applied to
Chart, not individual marks - [ ]
foregroundStyle(by:)used for categorical series (not manual per-mark colors) - [ ] Single-value selection uses
chartXSelection(value:)orchartYSelection(value:) - [ ] Range selection uses
chartXSelection(range:)orchartYSelection(range:) - [ ]
SectorMarkselection useschartAngleSelection(value:) - [ ] iOS 17+, iOS 18+, and iOS 26+ APIs are guarded with
#available - [ ]
.value()labels are descriptive for VoiceOver and Audio Graph accessibility