New in SwiftUI 4: Multi-Series Bar Chart

DevTechie Inc
Jun 25


Photo by Clay Banks on Unsplash

This year at WWDC22 Apple introduced Apple Charts, a component driven declarative way of building data visualization.

In this article, we will continue our exploration of Charts framework. If you need a refresher, please refer to this link.

Today, we will build multi-series bar chart. Multi-series bar chart extends the bar chart by plotting numeric values for levels of two categorical variables instead of one. Bars are grouped by position for levels of one categorical variable, with color indicating the secondary category level within each group.

A multi-series bar chart is used when you want to look at how the second category variable changes within each level of the first, or when you want to look at how the first category variable changes across levels of the second.

To understand this better, we will build an example. By the end of this article, we will have a bar chart like this:

We will start with a data structure to capture workout session for each day.

struct Workout: Identifiable {
    let id = UUID()
    let day: String
    let minutes: Int
}
We will add some sample data for two workout sessions, one for walking and another for running.

extension Workout {
    static let walkWorkout: [Workout] = [
        .init(day: "Mon", minutes: 23),
        .init(day: "Tue", minutes: 35),
        .init(day: "Wed", minutes: 55),
        .init(day: "Thu", minutes: 30),
        .init(day: "Fri", minutes: 15),
        .init(day: "Sat", minutes: 65),
        .init(day: "Sun", minutes: 81),
    ]
    
    static let runWorkout: [Workout] = [
        .init(day: "Mon", minutes: 16),
        .init(day: "Tue", minutes: 12),
        .init(day: "Wed", minutes: 55),
        .init(day: "Thu", minutes: 34),
        .init(day: "Fri", minutes: 22),
        .init(day: "Sat", minutes: 43),
        .init(day: "Sun", minutes: 90),
    ]
}
Now is the time to build our bar chart. We will start by creating an array of Tuples to store workout data:

let workoutData = [
    (workoutType: "Walk", data: Workout.walkWorkout),
    (workoutType: "Run", data: Workout.runWorkout)
]
Next, we will import Charts framework and add Chart view:

import Charts
import SwiftUI

struct ContentView: View {
    
    let workoutData = [
        (workoutType: "Walk", data: Workout.walkWorkout),
        (workoutType: "Run", data: Workout.runWorkout)
    ]
    
    var body: some View {
        VStack {
            Text("DevTechie.com")
                .font(.largeTitle)
                .foregroundColor(.primary)
            Chart {
               
            }
        }
        .padding()
    }
}
In order for us to display multi-series bar chart we will use nested ForEach loops. One ForEach will go over our array of Tuples and another will iterate over the data in each element:

struct ContentView: View {
    
    let workoutData = [
        (workoutType: "Walk", data: Workout.walkWorkout),
        (workoutType: "Run", data: Workout.runWorkout)
    ]
    
    var body: some View {
        VStack {
            Text("DevTechie.com")
                .font(.largeTitle)
                .foregroundColor(.primary)
            Chart {
                ForEach(workoutData, id: \.workoutType) { element in
                    ForEach(element.data) {
                        BarMark(x: .value("Day", $0.day), y: .value("Workout(in minutes)", $0.minutes))
                            
                    }
                    .foregroundStyle(by: .value("Workout(type)", element.workoutType))
                }
            }
        }
        .padding()
    }
}
This will create a stacked bar chart for us but if we use .position modifier and position BarMarks by the workout type, we will get a multi-series bars next to each other.

struct ContentView: View {
    
    let workoutData = [
        (workoutType: "Walk", data: Workout.walkWorkout),
        (workoutType: "Run", data: Workout.runWorkout)
    ]
    
    var body: some View {
        VStack {
            Text("DevTechie.com")
                .font(.largeTitle)
                .foregroundColor(.primary)
            Chart {
                ForEach(workoutData, id: \.workoutType) { element in
                    ForEach(element.data) {
                        BarMark(x: .value("Day", $0.day), y: .value("Workout(in minutes)", $0.minutes))
                            
                    }
                    .foregroundStyle(by: .value("Workout(type)", element.workoutType))
                    .position(by: .value("Workout(type)", element.workoutType))
                }
            }
        }
        .padding()
    }
}


With that we have reached the end of this article. Thank you once again for reading. Subscribe our newsletter at https://www.devtechie.com