Adding Charts to SwiftUI: A Practical Guide
Charts play a key role when it comes to turning raw data into something people can actually understand. Whether you're tracking user activity, visualizing growth, or summarizing analytics, charts help communicate complex information quickly and clearly.
SwiftUI already gives you a powerful way to build clean, modern interfaces. And with Apple's Charts library, bringing interactive and visually rich charts into your iOS apps feels like a natural extension of the SwiftUI workflow - not an extra chore.
In this guide, we'll walk through how to integrate charts into SwiftUI applications, build different chart types like bar charts, line charts, and pie-style charts, and tweak their appearance so they fit seamlessly into your app's design. The goal is simple: help you move from data to insight with minimal effort and maximum clarity.
Let's start building.
Importing the Charts Library
Apple introduced the Charts framework starting from iOS 16. It's built specifically for SwiftUI, so it fits naturally into the declarative UI flow you're already using.
First, make sure your project meets these requirements:
- iOS 16 or later
- SwiftUI-based app
- Xcode 14+
Then, simply import Charts wherever you plan to use it:
import Charts
That's it. No external dependencies, no package managers, no setup headaches.
Creating a Simple Bar Chart
Let's start with something simple and practical - a bar chart. Bar charts are usually the first choice when you want to compare values across categories, like monthly sales, usage stats, or feature adoption.
Suppose you want to show how sales performed over the first few months of the year. With SwiftUI and the Charts library, you can set this up with very little code:
struct BarChartView: View {
var body: some View {
Chart {
BarMark(
x: .value("X", 1),
y: .value("Y", 10)
)
BarMark(
x: .value("X", 2),
y: .value("Y", 20)
)
BarMark(
x: .value("X", 3),
y: .value("Y", 30)
)
BarMark(
x: .value("X", 4),
y: .value("Y", 40)
)
BarMark(
x: .value("X", 5),
y: .value("Y", 50)
)
BarMark(
x: .value("X", 6),
y: .value("Y", 60)
)
}
.frame(height: 300)
.padding()
}
}
Here's what's happening under the hood:
Chartacts as the container that holds and renders your chart.BarMarktells SwiftUI that you want to display the data as vertical bars.- Each tuple in the data array represents a single bar:
- The first value maps to the x-axis (for example, months).
- The second value maps to the y-axis (such as sales numbers).
SwiftUI automatically handles layout, scaling, and axis rendering for you. You don't need to manually calculate positions or sizes - the chart adapts based on the data you provide. This makes bar charts a great starting point when you want quick, readable visualizations without a lot of setup.
Once you're comfortable with this pattern, you can easily extend it to real-world data coming from APIs, databases, or user input.
Creating Other Types of Charts
Once you understand one chart type, the rest feel familiar. You mostly change the mark.
Line Chart (Great for Trends)
Perfect for showing progress over time.
var body: some View {
Chart(data) { item in
LineMark(
x: .value("Day", item.day),
y: .value("Value", item.value)
)
.foregroundStyle(.blue)
.lineStyle(StrokeStyle(lineWidth: 3))
PointMark( // optional: show dots on points
x: .value("Day", item.day),
y: .value("Value", item.value)
)
.foregroundStyle(.blue)
}
.frame(height: 300)
.padding()
}
Line charts are ideal for things like:
- Growth metrics
- Performance tracking
- Time-based analytics
Pie Chart (Proportions and Distribution)
let data: [Country] = [
Country(name: "India", population: 1428),
Country(name: "China", population: 1412),
Country(name: "USA", population: 339),
Country(name: "Indonesia", population: 277),
Country(name: "Pakistan", population: 240),
Country(name: "Brazil", population: 216)
]
var body: some View {
Chart(data) { item in
SectorMark(
angle: .value("Population", item.population)
)
.foregroundStyle(by: .value("Country", item.name))
}
.frame(height: 350)
.padding()
}
Use pie charts sparingly. They're best when comparing parts of a whole, not precise values.
Scatter Plot (Finding Patterns)
Scatter plots are useful when comparing two continuous variables.
var body: some View {
Chart(sampleData) { dataPoint in
PointMark(
x: .value("Hours Used", dataPoint.dailyHours),
y: .value("Social Battery %", dataPoint.socialBattery)
)
}
.frame(height: 300) // Set a frame height for better display
.padding()
}
These are great for:
- Identifying outliers
- Spotting correlations
- Visualizing raw data points
You can choose the chart type that best suits your data visualization needs.
Customizing the Look and Feel of Your Charts
Once your chart is working, the next question is always the same: "How do I make this match my app's design?"
That's where customization comes in.
SwiftUI's Charts library gives you a lot of control over how your charts look - colors, text styles, and overall presentation - without turning your code into a mess.
Here's a simple example of customizing a bar chart:
var body: some View {
Chart(data, id: \.x) { item in
BarMark(
x: .value("X Value", item.x),
y: .value("Y Value", item.y)
)
.foregroundStyle(Color.red) // Fill color
.clipShape(RoundedRectangle(cornerRadius: 4))
.annotation(position: .overlay) { // Stroke workaround
Rectangle()
.stroke(Color.black, lineWidth: 1)
}
}
.chartXAxis {
AxisMarks { _ in
AxisValueLabel()
.font(.system(size: 12)) // Axis label font
}
}
.chartYAxis {
AxisMarks { _ in
AxisValueLabel()
.font(.system(size: 12))
}
}
.frame(height: 300)
.padding()
}
What this customization does:
-
Bar color: Each bar is styled with a red fill using
foregroundStyle(Color.red). This helps the data stand out instantly and keeps the chart visually focused on the values. -
Rounded bar edges: The
clipShape(RoundedRectangle(cornerRadius: 4))adds subtle rounded corners to the bars. It's a small touch, but it makes the chart look cleaner and more modern. -
Bar outlines for clarity: Since Charts doesn't provide a direct stroke modifier for bars, an overlay annotation is used to draw a black border around each bar. This improves visual separation, especially when bars are close in value.
-
X-axis labels: The X-axis is customized using
chartXAxiswithAxisMarks. The label font is set to a smaller system font, keeping it readable without overwhelming the chart. -
Y-axis labels: The Y-axis follows the same approach as the X-axis, maintaining visual consistency and ensuring values are easy to scan at a glance.
-
Layout and spacing: The chart is given a fixed height of 300 points and padded on all sides. This prevents crowding and ensures the chart fits comfortably within the UI.
These small tweaks go a long way. They help your charts feel like a natural part of your app instead of something that looks bolted on. Whether you're matching a brand color palette or improving readability, SwiftUI makes it easy to fine-tune charts without overcomplicating your layout.
Once you're comfortable with these basics, you can layer in more advanced styling to create charts that are both functional and visually polished.
Conclusion
We've walked through how charts fit into SwiftUI apps using the Charts library—from setting things up to building bar charts, line charts, pie charts, and scatter plots, and finally shaping them to match your app's design. Each chart type serves a purpose, and when used thoughtfully, they turn raw numbers into something users can actually understand.
The best approach is to start small. Add a simple bar chart. Make sure it's clear and readable. Then, as your app grows, experiment with lines, points, and sectors where they make sense. Charts should guide users, not overwhelm them—clarity always matters more than visual flair.
When done right, charts don't just display data. They help users see patterns, understand trends, and make confident decisions. And that's where good design and good data meet.
Happy coding.
