Swift Charts Accessibility, Fallback, and Resources
Swift Charts Accessibility, Fallback, and Resources
Section titled “Swift Charts Accessibility, Fallback, and Resources”Table of Contents
Section titled “Table of Contents”Accessibility
Section titled “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
Section titled “Meaningful Labels”Always use clear, descriptive strings in .value(_, _) calls. These labels are read by VoiceOver and used in the Audio Graph.
// Good — descriptive labelsLineMark( x: .value("Date", entry.date), y: .value("Daily Steps", entry.count))
// Bad — generic labelsLineMark( x: .value("X", entry.date), y: .value("Y", entry.count))Custom Audio Graphs
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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