Create a line chart with SwiftUI Charts in iOS 16

Create a line chart with SwiftUI Charts in iOS 16

Apple introduced SwiftUI Charts at WWWDC 2022, which makes it incredibly easy to create charts within SwiftUI views. Charts are a great way of presenting visual data in a rich format that is easy to understand. This article shows how to easily create a line chart with so much less code than was previously used to create the same line chart from scratch. It is also so much easier to customise the look and feel of the chart as well as make the information in the chart accessible.

It is possible to create a line chart without using SwiftUI Charts as shown in previous articles. However, the use of Charts framework makes it so much easier by providing a large variety of charts to explore what works best for the data in an App.

Here are previous articles on creating Bar and Line charts from scratch in SwiftUI.



Simple Line chart

Start with data containing step counts for a week similar to data used in Create a line chart in SwiftUI. Define a structure to hold the date and the step count for that day and create an array of these for the current week.

 1struct StepCount: Identifiable {
 2    let id = UUID()
 3    let weekday: Date
 4    let steps: Int
 5    
 6    init(day: String, steps: Int) {
 7        let formatter = DateFormatter()
 8        formatter.dateFormat = "yyyyMMdd"
 9        
10        self.weekday = formatter.date(from: day) ?? Date.distantPast
11        self.steps = steps
12    }
13}
14
15
16let currentWeek: [StepCount] = [
17    StepCount(day: "20220717", steps: 4200),
18    StepCount(day: "20220718", steps: 15000),
19    StepCount(day: "20220719", steps: 2800),
20    StepCount(day: "20220720", steps: 10800),
21    StepCount(day: "20220721", steps: 5300),
22    StepCount(day: "20220722", steps: 10400),
23    StepCount(day: "20220723", steps: 4000)
24]

To create a Line Chart, create a Chart with a LineMark for each element in the step count data. Specify the Weekday for the x value and the step count for the y value for the LineMark. Note that it is also necessary to import the Charts framework.

That's it. This creates a line chart for the step count data. As there is only one series of data, the ForEach can be left out and the data can be passed directly to the Chart initializer. Both sections produce the same line chart.

 1import SwiftUI
 2import Charts
 3
 4struct LineChart1: View {
 5    var body: some View {
 6        VStack {
 7            GroupBox ( "Line Chart - Step Count") {
 8                Chart {
 9                    ForEach(currentWeek) {
10                        LineMark(
11                            x: .value("Week Day", $0.weekday, unit: .day),
12                            y: .value("Step Count", $0.steps)
13                        )
14                    }
15                }
16            }
17            
18            GroupBox ( "Line Chart - Step Count") {
19                Chart(currentWeek) {
20                    LineMark(
21                        x: .value("Week Day", $0.weekday, unit: .day),
22                        y: .value("Step Count", $0.steps)
23                    )
24                    
25                }
26            }
27        }
28    }
29}

Line charts created with SwiftUI Charts showing daily step counts



Other charts

There are a number of charting options available from SwiftUI Charts. These can be generated by changing the chart marks from LineMark to one of the other types of marks such as BarMark for a Bar Chart.

 1struct OtherCharts: View {
 2    var body: some View {
 3        VStack {
 4            GroupBox ( "Line Chart - Step count") {
 5                Chart(currentWeek) {
 6                    LineMark(
 7                        x: .value("Week Day", $0.weekday, unit: .day),
 8                        y: .value("Step Count", $0.steps)
 9                    )
10                }
11            }
12            
13            GroupBox ( "Bar Chart - Step count") {
14                Chart(currentWeek) {
15                    BarMark(
16                        x: .value("Week Day", $0.weekday, unit: .day),
17                        y: .value("Step Count", $0.steps)
18                    )
19                }
20            }
21            
22            GroupBox ( "Point Chart - Step count") {
23                Chart(currentWeek) {
24                    PointMark(
25                        x: .value("Week Day", $0.weekday, unit: .day),
26                        y: .value("Step Count", $0.steps)
27                    )
28                }
29            }
30            
31            GroupBox ( "Rectangle Chart - Step count") {
32                Chart(currentWeek) {
33                    RectangleMark(
34                        x: .value("Week Day", $0.weekday, unit: .day),
35                        y: .value("Step Count", $0.steps)
36                    )
37                }
38            }
39            
40            GroupBox ( "Area Chart - Step count") {
41                Chart(currentWeek) {
42                    AreaMark(
43                        x: .value("Week Day", $0.weekday, unit: .day),
44                        y: .value("Step Count", $0.steps)
45                    )
46                }
47            }
48        }
49    }
50}

Other chart types created with SwiftUI Charts showing daily step counts



Make Line Chart Accessible

One advantage of having Charts baked into SwiftUI is that it is easy to make the charts accessibile using Accessibility modifiers. Add a computed property to StepCount to return the data as a string, which can be used by accessibilityLabel. Then add accessibility label and value for each mark in the chart.

 1struct StepCount: Identifiable {
 2    let id = UUID()
 3    let weekday: Date
 4    let steps: Int
 5    
 6    init(day: String, steps: Int) {
 7        let formatter = DateFormatter()
 8        formatter.dateFormat = "yyyyMMdd"
 9        
10        self.weekday = formatter.date(from: day) ?? Date.distantPast
11        self.steps = steps
12    }
13    
14    var weekdayString: String {
15        let dateFormatter = DateFormatter()
16        dateFormatter.dateFormat = "yyyyMMdd"
17        dateFormatter.dateStyle = .long
18        dateFormatter.timeStyle = .none
19        dateFormatter.locale = Locale(identifier: "en_US")
20        return  dateFormatter.string(from: weekday)
21    }
22}
 1    GroupBox ( "Line Chart - Daily Step Count") {
 2        Chart(currentWeek) {
 3            LineMark(
 4                x: .value("Week Day", $0.weekday, unit: .day),
 5                y: .value("Step Count", $0.steps)
 6            )
 7            .accessibilityLabel($0.weekdayString)
 8            .accessibilityValue("\($0.steps) Steps")
 9        }
10    }

Make line chart accessibile in SwiftUI Charts



Add multiple data series to Line Chart

A line chart is a great way to compare two different series of data. Create a second series of step counts for the previous week and add both series to a line chart.

 1let previousWeek: [StepCount] = [
 2    StepCount(day: "20220710", steps: 15800),
 3    StepCount(day: "20220711", steps: 7300),
 4    StepCount(day: "20220712", steps: 8200),
 5    StepCount(day: "20220713", steps: 25600),
 6    StepCount(day: "20220714", steps: 16100),
 7    StepCount(day: "20220715", steps: 16500),
 8    StepCount(day: "20220716", steps: 3200)
 9]
10
11let currentWeek: [StepCount] = [
12    StepCount(day: "20220717", steps: 4200),
13    StepCount(day: "20220718", steps: 15000),
14    StepCount(day: "20220719", steps: 2800),
15    StepCount(day: "20220720", steps: 10800),
16    StepCount(day: "20220721", steps: 5300),
17    StepCount(day: "20220722", steps: 10400),
18    StepCount(day: "20220723", steps: 4000)
19]
20
21let stepData = [
22    (period: "Current Week", data: currentWeek),
23    (period: "Previous Week", data: previousWeek)
24]

The first attempt to add these two series of data does not display as expected.

 1struct LineChart2: View {
 2    var body: some View {
 3        GroupBox ( "Line Chart - Daily Step Count") {
 4            Chart {
 5                ForEach(stepData, id: \.period) {
 6                    ForEach($0.data) {
 7                        LineMark(
 8                            x: .value("Week Day", $0.weekday, unit: .day),
 9                            y: .value("Step Count", $0.steps)
10                        )
11                        .accessibilityLabel($0.weekdayString)
12                        .accessibilityValue("\($0.steps) Steps")
13                    }
14                }
15            }
16        }
17    }
18}

First attempt to create a line chart in SwiftUI Charts with two series of step count data



Display multiple series of step count based on weekday in Line Chart

The issue with the initial attempt to display multiple sets of data in a line chart is the use of date for the x-axis. The current wee follows on from the previous week so each point is plotted in a linear progression along the x-axis.

It is necessary to use just the weekday as the x-axis values, so that all Sundays are plotted at the same x-coordinate.

Add another computed property to the StepCount to return the short day in a string format for the weekday date.

 1struct StepCount: Identifiable {
 2
 3    . . .
 4    
 5        
 6    var shortDay: String {
 7        let dateFormatter = DateFormatter()
 8        dateFormatter.dateFormat = "EEE"
 9        return  dateFormatter.string(from: weekday)
10    }
11}

This shortDay is used for the x-value of the LineMarks in the chart. In addition, the style of the foreground is set to be based on the period of the stepCount array. The line chart displays the step counts for both weeks using the weekdays for the x-axis for easy comparison between the weeks.

 1struct LineChart3: View {
 2    var body: some View {
 3        VStack {
 4            GroupBox ( "Line Chart - Daily Step Count") {
 5                Chart {
 6                    ForEach(stepData, id: \.period) { steps in
 7                        ForEach(steps.data) {
 8                            LineMark(
 9                                x: .value("Week Day", $0.shortDay),
10                                y: .value("Step Count", $0.steps)
11                            )
12                            .foregroundStyle(by: .value("Week", steps.period))
13                            .accessibilityLabel($0.weekdayString)
14                            .accessibilityValue("\($0.steps) Steps")
15                        }
16                    }
17                }
18                .frame(height:400)
19            }
20            .padding()
21
22            Spacer()
23        }
24    }
25}

Line chart in SwiftUI Charts with two series of step count data




Conclusion

There is so much more to explore in SwiftUI Charts. It is clearly better to use this framework rather than building your own charts from scratch.