Every now and then I need to visualise data in some nice-looking charts. This post shows how to draw charts in a SwiftUI application.
Starting a charting package from scratch was not an option due to time and budget constraints, so I looked for existing solutions.
My vote went to SwiftUI-Charts, which offers nice-looking graphs and easy integration.
Setup and Project Configuration
We start by creating a project in Xcode.

The starting point is the standard SwiftUI application that will be modified.

Next, add the package by opening the project settings.

Press the “plus” button to add a new package. Enter the repository URL: https://github.com/spacenation/swiftui-charts.git.

On the next screen I left the defaults.

In the final screen I also accepted the default values.

Displaying Constant Data
Time to add code. For a first test I used a snippet from the GitHub README and pasted it into ContentView:
import Charts
import SwiftUI
struct ContentView: View {
var body: some View {
Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1])
.chartStyle(
LineChartStyle(.quadCurve, lineColor:.blue, lineWidth: 5)
)
}
}Running this in the simulator draws a graph of the constant values.

This qualifies as a successful first test.
Displaying Dynamic Data
In my work the data I visualise changes over time (for example, sensor input). Let’s extend the example to update the graph dynamically.
The “sensor” will be a class conforming to ObservableObject that publishes a random Double every 500 milliseconds.
import Foundation
class ValuePublisher: ObservableObject {
@Published var value: Double = 0.0
init() {
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
self.value = Double.random(in: 0...1.0)
}
}
}Instantiate this in ContentView as a @StateObject:
@StateObject var valuePublisher = ValuePublisher()ValuePublisher emits single values, but we need a list of values. A simple queue structure does the trick.
struct Queue<T> {
var list = [T]()
mutating func enqueue(_ element: T) {
list.append(element)
}
mutating func dequeue() -> T? {
if!list.isEmpty {
return list.removeFirst()
} else {
return nil
}
}
func peek() -> T? {
if!list.isEmpty {
return list[0]
} else {
return nil
}
}
}Instantiate the queue as a @State variable in ContentView:
@State var doubleQueue = Queue<Double>()Initialize the underlying list when the view appears:
.onAppear {
doubleQueue.list = [Double](repeating: 0.0, count: 50)
}Tell the chart to use the list of values:
Chart(data: doubleQueue.list)Finally, append published values from ValuePublisher to the queue and remove the oldest value:
.onChange(of: valuePublisher.value) { value in
self.doubleQueue.enqueue(value)
_ = self.doubleQueue.dequeue()
}That’s about it. Here is the full ContentView where I also adjusted the graph appearance:
import Charts
import SwiftUI
struct ContentView: View {
@State var doubleQueue = Queue<Double>()
@StateObject var valuePublisher = ValuePublisher()
var body: some View {
Chart(data: doubleQueue.list)
.chartStyle(
AreaChartStyle(.quadCurve, fill:
LinearGradient(gradient:.init(colors: [Color.blue.opacity(1.0), Color.blue.opacity(0.5)]), startPoint:.top, endPoint:.bottom)
)
)
.onAppear {
doubleQueue.list = [Double](repeating: 0.0, count: 50)
}
.onChange(of: valuePublisher.value) { value in
self.doubleQueue.enqueue(value)
_ = self.doubleQueue.dequeue()
}
.padding()
}
}Screenshot of the final application:

I also uploaded a video showing the values updating dynamically:
Feel free to buy me a coffee if you liked this post.