- Published on
- 6 min read
> Getting Started with Swift Charts
Apple introduced Swift Charts at WWDC 2022, giving SwiftUI developers a declarative way to build data visualizations. The framework follows SwiftUI's compositional approach, letting you construct charts from simple building blocks called marks.
Your First Chart
Creating a chart starts with the Chart view and one or more marks. Here's a simple bar chart:
import SwiftUI
import Charts
struct MonthlySales: Identifiable {
let id = UUID()
let month: String
let revenue: Double
}
struct SalesChartView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000),
MonthlySales(month: "May", revenue: 9500),
MonthlySales(month: "Jun", revenue: 11000)
]
var body: some View {
Chart(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
}
.frame(height: 300)
.padding()
}
}
The Chart view takes a collection and a closure that returns marks for each data point. BarMark creates rectangular bars, with x and y defining the axes. The .value() function labels each axis for accessibility and tooltips.
Types of Marks
Swift Charts provides several mark types for different visualizations:
struct MultipleMarksView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000)
]
var body: some View {
VStack(spacing: 40) {
Chart(data) { item in
LineMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
}
.frame(height: 150)
Chart(data) { item in
PointMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
}
.frame(height: 150)
Chart(data) { item in
AreaMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
}
.frame(height: 150)
}
.padding()
}
}
LineMark connects points with lines, PointMark shows individual dots, and AreaMark fills the area under the line. You can combine multiple mark types in the same chart for richer visualizations.
Combining Marks
Layering marks creates more informative charts. Here's a line chart with points highlighted:
struct CombinedMarksView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000)
]
var body: some View {
Chart(data) { item in
LineMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(.blue)
PointMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(.blue)
.symbolSize(100)
}
.frame(height: 300)
.padding()
}
}
Working with Multiple Series
Charts often need to display multiple data series. Use foregroundStyle(by:) to differentiate them:
struct SalesByCategory: Identifiable {
let id = UUID()
let month: String
let category: String
let revenue: Double
}
struct MultiSeriesChartView: View {
let data: [SalesByCategory] = [
SalesByCategory(month: "Jan", category: "Electronics", revenue: 5000),
SalesByCategory(month: "Jan", category: "Clothing", revenue: 3000),
SalesByCategory(month: "Feb", category: "Electronics", revenue: 8000),
SalesByCategory(month: "Feb", category: "Clothing", revenue: 4500),
SalesByCategory(month: "Mar", category: "Electronics", revenue: 7500),
SalesByCategory(month: "Mar", category: "Clothing", revenue: 5200),
SalesByCategory(month: "Apr", category: "Electronics", revenue: 12000),
SalesByCategory(month: "Apr", category: "Clothing", revenue: 6800)
]
var body: some View {
Chart(data) { item in
LineMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(by: .value("Category", item.category))
.symbol(by: .value("Category", item.category))
}
.frame(height: 300)
.padding()
}
}
Swift Charts automatically generates a legend and assigns distinct colors to each category.
Customizing Appearance
You can style charts with familiar SwiftUI modifiers:
struct StyledChartView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000)
]
var body: some View {
Chart(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(
.linearGradient(
colors: [.blue, .purple],
startPoint: .bottom,
endPoint: .top
)
)
.cornerRadius(8)
}
.chartYAxis {
AxisMarks(position: .leading)
}
.chartXAxis {
AxisMarks { value in
AxisValueLabel()
.font(.caption)
}
}
.frame(height: 300)
.padding()
}
}
The chartYAxis and chartXAxis modifiers give you control over axis appearance, positioning, and labels.
Date-Based Charts
For time series data, use Date values on the x-axis:
struct DailyMetric: Identifiable {
let id = UUID()
let date: Date
let value: Double
}
struct TimeSeriesChartView: View {
let data: [DailyMetric]
init() {
let calendar = Calendar.current
let today = Date()
data = (0..<14).map { dayOffset in
let date = calendar.date(byAdding: .day, value: -dayOffset, to: today)!
let value = Double.random(in: 100...500)
return DailyMetric(date: date, value: value)
}.reversed()
}
var body: some View {
Chart(data) { item in
LineMark(
x: .value("Date", item.date, unit: .day),
y: .value("Value", item.value)
)
.interpolationMethod(.catmullRom)
AreaMark(
x: .value("Date", item.date, unit: .day),
y: .value("Value", item.value)
)
.foregroundStyle(.blue.opacity(0.1))
.interpolationMethod(.catmullRom)
}
.chartXAxis {
AxisMarks(values: .stride(by: .day, count: 3)) { value in
AxisGridLine()
AxisValueLabel(format: .dateTime.month().day())
}
}
.frame(height: 300)
.padding()
}
}
The unit: .day parameter tells the chart to treat dates as discrete days. The interpolationMethod smooths the line between points.
Adding Interactivity
Swift Charts supports selection through the chartOverlay or chartGesture modifiers:
struct InteractiveChartView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000),
MonthlySales(month: "May", revenue: 9500),
MonthlySales(month: "Jun", revenue: 11000)
]
@State private var selectedMonth: String?
var body: some View {
VStack {
Chart(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(selectedMonth == item.month ? .blue : .gray)
}
.chartXSelection(value: $selectedMonth)
.frame(height: 300)
if let month = selectedMonth,
let item = data.first(where: { $0.month == month }) {
Text("\(month): $\(Int(item.revenue))")
.font(.headline)
.padding()
}
}
.padding()
}
}
The chartXSelection modifier binds a selection state to tap or drag gestures on the chart.
Rule Marks for Reference Lines
Use RuleMark to add reference lines like averages or thresholds:
struct ChartWithReferenceLineView: View {
let data: [MonthlySales] = [
MonthlySales(month: "Jan", revenue: 5000),
MonthlySales(month: "Feb", revenue: 8000),
MonthlySales(month: "Mar", revenue: 7500),
MonthlySales(month: "Apr", revenue: 12000),
MonthlySales(month: "May", revenue: 9500),
MonthlySales(month: "Jun", revenue: 11000)
]
var average: Double {
data.map(\.revenue).reduce(0, +) / Double(data.count)
}
var body: some View {
Chart {
ForEach(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.foregroundStyle(.blue)
}
RuleMark(y: .value("Average", average))
.foregroundStyle(.orange)
.lineStyle(StrokeStyle(lineWidth: 2, dash: [5, 5]))
.annotation(position: .top, alignment: .leading) {
Text("Avg: $\(Int(average))")
.font(.caption)
.foregroundStyle(.orange)
}
}
.frame(height: 300)
.padding()
}
}
Accessibility
Swift Charts automatically provides VoiceOver support through the labels you pass to .value(). You can enhance this with the accessibilityLabel modifier:
struct AccessibleChartView: View {
let data: [MonthlySales] = [
MonthlySales(month: "January", revenue: 5000),
MonthlySales(month: "February", revenue: 8000),
MonthlySales(month: "March", revenue: 7500)
]
var body: some View {
Chart(data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Revenue", item.revenue)
)
.accessibilityLabel("\(item.month)")
.accessibilityValue("$\(Int(item.revenue)) in revenue")
}
.frame(height: 300)
.padding()
}
}
VoiceOver users can swipe through each bar and hear the month and revenue values.
Swift Charts handles most of the complexity of data visualization for you. Define your data, choose appropriate marks, and let the framework manage scales, legends, and layout. For more advanced customizations like custom scales, annotations, or animations, check Apple's Swift Charts documentation.
Sample Project
Want to see this code in action? Check out the complete sample project on GitHub:
The repository includes a working Xcode project with all the examples from this article, plus unit tests you can run to verify the behavior.
// Continue_Learning
Building Interactive Glass Controls in SwiftUI
Make your glass elements respond to touch with scaling, shimmer effects, and touch-point illumination using the interactive() modifier and glass button styles.
Creating Morphing Glass Transitions with glassEffectID
The glassEffectID modifier enables glass views to smoothly morph into one another during state changes, creating fluid transitions that feel native to iOS 26.
Coordinating Glass Elements with GlassEffectContainer
When you have multiple glass elements that should blend and animate together, GlassEffectContainer coordinates their rendering for seamless visual results.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.