Back to Blogs

Swift III: A beginner’s guide to Swift Charts

iOS App Development
Article
Technology, Information and Media
What would developers do if Swift never existed? Act like disgruntled pelicans?

Jokes aside, Swift has become the vital tool of choice for developers for the benefits it brings to the table. And what works even more in Swift’s favor is the regular addition of exciting tools and features. Swift Charts is one such feature that was introduced at WWDC 2022

Data visualization has always helped simplify complex data sets while enhancing presentation. And now, with the help of Swift Charts, developers can implement the same in their apps. 

Introduction to Swift Charts

So, what is Swift Charts? It is a powerful, flexible framework that can create aesthetically pleasing charts using little code. Like SwiftUI, it uses a declarative syntax. Hence, developers familiar with SwiftUI can seamlessly use Swift Charts. 

Earlier, developers had to contend with external libraries for creating charts in their apps, but not anymore. 

Swift Charts also provides out-of-the-box support for dark mode, device screen sizes, dynamic types, voiceover, audio graphs, localization, and multi-platform support. This exciting framework is available for Xcode 14, while also providing support for iOS 16+, macOS 13+, Mac Catalyst 16+, tvOS 16+, and watchOS 9+. 

Exploring Swift Charts

Swift Charts is fun and exciting to use, and it comes with six default chart types called Marks. The six chart types are bar, line, point, area, rule, and rectangle. Additionally, these charts are extensible; hence they can be customized.

Now, let's explore these charts with the help of an example. 

Sip n Snacks is an Indian fast food restaurant that serves three types of dishes—dosa, paratha, and chaat. This restaurant is open to customers during morning and evening hours. So, let’s analyze their sales data during the weekdays with the help of charts. This analysis can help the restaurant to plan its staffing and procurement for the week as per the sales trends.

Let’s start by creating all the enum types that would be required:

  • FoodItemType enum with cases for dosa, paratha, and chaat as food items for sale.
  • WeekDaysType enum with a case for each day of the week is created. 
  • TimingType enum with cases for morning and evening hours.
  • ChartType enum with cases for different chart types in Swift Charts.

 So, the image below shows the model code we will be implementing.

 <script>
struct RestaurantSales: Identifiable {
    let weekday: WeekdaysType
    let itemSales: [ItemSales]
    var id: String {
        return weekday.rawValue
    }
}
struct ItemSales: Identifiable {
    let foodItemName: FoodItemType
    let sales: Int
    var id: String {
        return foodItemName.rawValue
    }
}
enum FoodItemType: String {
    case Dosa, Paratha, Chaat
}
enum WeekdaysType: String {
    case Mon, Tue, Wed, Thu, Fri, Sat, Sun
}
enum TimingType: String {
    case Morning, Evening
}
enum ChartType: String {
    case Barchart, Linechart, Pointchart, Areachart, Rulechart, Rectanglechart
}
 </script> 

Code block 1: Model code

Next, we’ll create two data sets for morning and evening hours. For this, we’ll create two constants—morningSales and eveningSales, which are arrays of RestaurantSales.

Sample data code for morningSales

 <script>
let morningSales : [RestaurantSales]  =
[.init(weekday: .Mon, itemSales: [.init(foodItemName: .Dosa, sales: 2),
                                  .init(foodItemName: .Paratha, sales: 5),
                                  .init(foodItemName: .Chaat, sales: 10)]),
 .init(weekday: .Tue, itemSales: [.init(foodItemName: .Dosa, sales: 4),
                                  .init(foodItemName: .Paratha, sales: 4),
                                  .init(foodItemName: .Chaat, sales: 13)]),
 .init(weekday: .Wed, itemSales: [.init(foodItemName: .Dosa, sales: 3),
                                  .init(foodItemName: .Paratha, sales: 5),
                                  .init(foodItemName: .Chaat, sales: 13)]),
 .init(weekday: .Thu, itemSales: [.init(foodItemName: .Dosa, sales: 5),
                                  .init(foodItemName: .Paratha, sales: 3),
                                  .init(foodItemName: .Chaat, sales: 18)]),
 .init(weekday: .Fri, itemSales: [.init(foodItemName: .Dosa, sales: 9),
                                  .init(foodItemName: .Paratha, sales: 1),
                                  .init(foodItemName: .Chaat, sales: 3)]),
 .init(weekday: .Sat, itemSales: [.init(foodItemName: .Dosa, sales: 6),
                                  .init(foodItemName: .Paratha, sales: 11),
                                  .init(foodItemName: .Chaat, sales: 25)]),
 .init(weekday: .Sun, itemSales: [.init(foodItemName: .Dosa, sales: 8),
                                  .init(foodItemName: .Paratha, sales: 10),
                                  .init(foodItemName: .Chaat, sales: 22)])
]
 </script> 

Code block 2: morningSales sample data

Sample data code for eveningSales

 <script>
let eveningSales : [RestaurantSales]  =
[.init(weekday: .Mon, itemSales: [.init(foodItemName: .Dosa, sales: 9),
                                  .init(foodItemName: .Paratha, sales: 15),
                                  .init(foodItemName: .Chaat, sales: 10)]),
 .init(weekday: .Tue, itemSales: [.init(foodItemName: .Dosa, sales: 10),
                                  .init(foodItemName: .Paratha, sales: 14),
                                  .init(foodItemName: .Chaat, sales: 23)]),
 .init(weekday: .Wed, itemSales: [.init(foodItemName: .Dosa, sales: 13),
                                  .init(foodItemName: .Paratha, sales: 15),
                                  .init(foodItemName: .Chaat, sales: 19)]),
 .init(weekday: .Thu, itemSales: [.init(foodItemName: .Dosa, sales: 15),
                                  .init(foodItemName: .Paratha, sales: 15),
                                  .init(foodItemName: .Chaat, sales: 22)]),
 .init(weekday: .Fri, itemSales: [.init(foodItemName: .Dosa, sales: 19),
                                  .init(foodItemName: .Paratha, sales: 10),
                                  .init(foodItemName: .Chaat, sales: 13)]),
 .init(weekday: .Sat, itemSales: [.init(foodItemName: .Dosa, sales: 16),
                                  .init(foodItemName: .Paratha, sales: 9),
                                  .init(foodItemName: .Chaat, sales: 15)]),
 .init(weekday: .Sun, itemSales: [.init(foodItemName: .Dosa, sales: 18),
                                  .init(foodItemName: .Paratha, sales: 11),
                                  .init(foodItemName: .Chaat, sales: 28)])
]
 </script> 

Code block 3: eveningSales sample data

So, now let’s explore the various types of charts. 

Bar charts

The process to create a new chart in Swift starts off by defining a chart block. Additionally, you can also pass a data array within the rounded brackets. In this example, it is an array of RestaurantSales. Since we have to loop through the days of the week, we have to define a ForEach block for the itemSales.

Now, we can create a bar chart with a BarMark block, which will accept x and y coordinates for the graph. In this case, x axis is the days of the week while y axis is the sales numbers. 

Since we need to show sales for the various food items, we’re adding a foregroundStyle modifier to the BarMark block with foodItemName. This will show different colors for each food item and Swift will automatically provide the colors for differentiation. 

 <script>
import SwiftUI
import Charts

struct Barchart: View {
    @Binding var timing: TimingType
    var data: [RestaurantSales]
    
    var body: some View {
        VStack(alignment : .leading) {
            Chart(data) { series in
                ForEach(series.itemSales) { item in
                    BarMark(
                        x: .value("Day", series.weekday.rawValue),
                        y: .value("Sales", item.sales)
                    )
                    .foregroundStyle(by: .value("Food", item.foodItemName.rawValue))
                }
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct Barchart_Previews: PreviewProvider {
    static var previews: some View {
        Barchart(timing: .constant(.Morning), data: morningSales)
    }
}
 </script> 

Code block 4: Bar chart code

Image 1: Bar chart iPhone preview

Line, point, and rule charts

Line charts in Swift are similar to bar charts with the only difference being the code block, which is shown as LineMark.

There is also an option to superimpose a point chart on a line chart by using a shorthand modifier to the LineMark code block. In this example, we are also superimposing another chart known as the RuleMark, with which we can represent a straight line in the x or y axes. Here, its being used to indicate a breakeven point in sales on the y axis. 

 <script>
import SwiftUI
import Charts

struct Linechart: View {
    @Binding var timing: TimingType
    var data: [RestaurantSales]
    var body: some View {
        VStack(alignment : .leading) {
            Chart(data) { series in
                ForEach(series.itemSales) { item in
                    LineMark(
                        x: .value("Day", series.weekday.rawValue),
                        y: .value("Sales", item.sales)
                    )
                    .foregroundStyle(by: .value("Food", item.foodItemName.rawValue))
                    .symbol(by: .value("Food", item.foodItemName.rawValue))
                }
                RuleMark(
                    y: .value("Breakeven", 15)
                )
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct Linechart_Previews: PreviewProvider {
    static var previews: some View {
        Linechart(timing: .constant(.Morning), data: morningSales)
    }
}
</script> 

Code block 5: Line chart code with symbol modifier and rule chart overlay

Image 2: Line chart with symbol modifier and rule chart overlay in iPhone preview

Area chart

Like the LineMark, we can also create an area chart with an AreaMark block. 

 <script>
import SwiftUI
import Charts

struct Areachart: View {
    @Binding var timing: TimingType
    var data: [RestaurantSales]
    
    var body: some View {
        VStack(alignment : .leading) {
            Chart(data) { series in
                ForEach(series.itemSales) { item in
                    AreaMark(
                        x: .value("Day", series.weekday.rawValue),
                        y: .value("Sales", item.sales)
                    )
                    .foregroundStyle(by: .value("Food", item.foodItemName.rawValue))
                }
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct Areachart_Previews: PreviewProvider {
    static var previews: some View {
        Areachart(timing: .constant(.Morning), data: morningSales)
    }
}
</script> 

Code block 6: Area chart code

Image 3: Area chart iPhone preview

 

Rectangle chart

Like the LineMark, we can also create an area chart with an RectangleMark block. 

 <script>
import SwiftUI
import Charts

struct Rectanglechart: View {
    @Binding var timing: TimingType
    var data: [RestaurantSales]
    
    var body: some View {
        VStack(alignment : .leading) {
            Chart(data) { series in
                ForEach(series.itemSales) { item in
                    RectangleMark(
                        x: .value("Day", series.weekday.rawValue),
                        y: .value("Sales", item.sales)
                    )
                    .foregroundStyle(by: .value("Food", item.foodItemName.rawValue))
                }
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct Rectanglechart_Previews: PreviewProvider {
    static var previews: some View {
        Rectanglechart(timing: .constant(.Morning), data: morningSales)
    }
}
</script> 

Code block 7: Rectangle chart code

Image 4: Rectangle chart iPhone preview

The beauty of Swift Charts

Now, lets take a look at all the charts on a single screen. This can make the visual differences between the various charts easy to understand. Swift Charts provides us with automatic animations during chart transitions, but we have implemented a custom easeIn animation in this case.

 <script>
import SwiftUI
import Charts

struct ContentView: View {
    @State var timing: TimingType = .Morning
    @State var chartType: ChartType = .Barchart
    var data: [RestaurantSales] {
        switch timing {
        case .Morning:
            return morningSales
        case .Evening:
            return eveningSales
        }
    }
    var body: some View {
        VStack(alignment:.leading) {
            Text("Select Chart")
                .font(.headline)
                .fontWeight(.bold)
            Picker("Select chart", selection: $chartType.animation(.easeIn)) {
                Text("Bar").tag(ChartType.Barchart)
                Text("Line").tag(ChartType.Linechart)
                Text("Point").tag(ChartType.Pointchart)
                Text("Area").tag(ChartType.Areachart)
                Text("Rule").tag(ChartType.Rulechart)
                Text("Rect").tag(ChartType.Rectanglechart)
            }
            .pickerStyle(.segmented)
            Text("Sip n Snacks")
                .font(.headline)
                .fontWeight(.bold)
                .padding(.top)
            Text("Sales analysis")
                .font(.subheadline)
                .fontWeight(.medium)
            Picker("Timing", selection: $timing.animation(.easeIn)) {
                Text("Morning").tag(TimingType.Morning)
                Text("Evening").tag(TimingType.Evening)
            }
            .pickerStyle(.segmented)
            switch chartType {
            case .Barchart:
                Barchart(timing: $timing, data: data)
            case .Linechart:
                Linechart(timing: $timing, data: data)
            case .Pointchart:
                Pointchart(timing: $timing, data: data)
            case .Areachart:
                Areachart(timing: $timing, data: data)
            case .Rulechart:
                Rulechart(timing: $timing, data: data)
            case .Rectanglechart:
                Rectanglechart(timing: $timing, data: data)
            }
            Spacer()
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
</script> 

Code block 8: All charts in one screen code

Image 5: Preview for all charts on iPhone

As you can see Swift Charts is fun to use and also adds an interesting feature in iOS apps, which helps enhance user experience. 

And that ends the three-part series of our adventures with Swift. So, if you’re looking for an app to be built for iOS, we’d love to hear from you and bounce ideas off together.

Sahil Satralkar

Sahil is an iOS engineer who constantly strives to improve his coding skills while exploring Swift programming. He enjoys watching Arsenal play and trying new fast-food joints in his spare time.

More by this author