SwiftUI makes it simple to create stunning, interactive data visualizations with its Charts framework, introduced in iOS 16. This article explores how to build dynamic line and bar graphs using Charts and Combine to simulate real-time data updates.
Overview of the Components
1. LineGraphSample View
This view displays a dynamic line chart that updates every two seconds with new data points. It uses the LineMark
API from the Charts framework to render the graph.
Key Features:
- Dynamic Updates: The chart dynamically reflects real-time data changes.
- Custom Styling: The line graph includes blue-colored lines and circular markers at each data point.
Code:
struct LineGraphSample: View {
@StateObject private var viewModel = GraphSampleViewModel()
var body: some View {
VStack {
Chart(viewModel.data) { item in
LineMark(
x: .value("Month", item.month),
y: .value("Sales", item.sales)
)
.foregroundStyle(.blue)
.symbol(Circle())
}
.padding(.top)
}
.navigationTitle("Dynamic Line Chart")
}
}
2. BarGraphSample View
The BarGraphSample
view uses the BarMark
API to create a bar chart that also dynamically updates every two seconds.
Key Features:
- Bar Visualization: Each bar represents sales for a specific month.
- Dynamic Data: Updates are reflected in real-time, making it ideal for representing temporal data trends.
Code:
struct BarGraphSample: View {
@ObservedObject var viewModel = GraphSampleViewModel()
var body: some View {
VStack {
Chart(viewModel.data) { item in
BarMark(
x: .value("Month", item.month),
y: .value("Sales", item.sales)
)
.foregroundStyle(.blue)
}
.navigationTitle("Bar Sales Charts")
.padding(.top)
}
}
}
3. GraphSampleViewModel
The GraphSampleViewModel
manages the data for both graphs. It uses Combine to generate random sales data for each month and updates the chart every two seconds.
Key Features:
- Randomized Data Generation: Simulates real-world scenarios with dynamic, ever-changing data.
- Timer with Combine: Uses Combine’s
Timer
publisher to add new data points periodically. - Rolling Data Window: Keeps only the latest 12 months of data for a clean visualization.
Code:
class GraphSampleViewModel: ObservableObject {
@Published var data: [GraphSampleSalesData] = []
private var cancellables = Set<AnyCancellable>()
private var timer: AnyCancellable?
private var currentMonth: Int = 0
init() {
startGeneratingData()
}
func startGeneratingData() {
addRandomData()
timer = Timer.publish(every: 2, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
self?.addRandomData()
}
}
private func addRandomData() {
if currentMonth > 11 {
currentMonth = 0
}
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
let randomMonth = months[currentMonth]
let randomSales = Double.random(in: 100...300)
currentMonth += 1
let newEntry = GraphSampleSalesData(month: randomMonth, sales: randomSales)
DispatchQueue.main.async {
if self.data.count >= 12 {
self.data.removeFirst()
}
self.data.append(newEntry)
}
}
}
4. GraphSampleSalesData
This simple struct represents each data point, including the month and corresponding sales value.
Code:
struct GraphSampleSalesData: Identifiable {
let id = UUID()
let month: String
let sales: Double
}
How It Works
- Dynamic Data Generation:
- The
GraphSampleViewModel
generates random sales data every two seconds. - It updates the
data
array, which is observed by the SwiftUI views.
- The
- Charts Framework:
- The
LineMark
andBarMark
APIs render the data as line and bar graphs, respectively. - SwiftUI automatically re-renders the chart when the data changes.
- The
- Combine Integration:
- A
Timer
publisher creates a periodic trigger to update the data array. - Combine’s
.sink
handles appending new data points to the array.
- A
Benefits of This Approach
- Real-Time Updates:
- Perfect for use cases like dashboards, stock monitoring, or performance metrics.
- Reusable Components:
- The
GraphSampleViewModel
can support multiple chart types with minimal adjustments.
- The
- Declarative and Dynamic:
- SwiftUI’s declarative nature makes it easy to build responsive, dynamic visualizations.
- Visual Simplicity:
- The Charts framework simplifies creating professional-quality graphs without third-party libraries.
Conclusion
Using SwiftUI’s Charts framework with Combine allows you to create dynamic and visually appealing graphs in minimal code. The LineGraphSample
and BarGraphSample
views demonstrate how to visualize time-series data effectively, while the GraphSampleViewModel
showcases a scalable way to manage and update data in real-time. These examples are perfect starting points for integrating dynamic data visualizations into your apps.
The Source Code Repository
Check out the source Github Repository:
https://github.com/san0suke/swift-ui-combine2/tree/main/SwiftUICombine/SwiftUICombine/GraphSample