Example in Charts documentation does not work as shown.

Summary: The "ProfitOverTime" example code from https://developer.apple.com/documentation/charts/chart does not compile, and when changes are made to make it compile the chart concatenates the lines rather than having them be separate.

Seen in Xcode-Beta's 14.3 and 14. 4 (14A5284g)

Is it simply that this method of layering lines is deprecated already?


When trying to have a chart with multiple data sets, I copy-pasted the code from the above link and generated the appropriate placeholder data. I got an error that ProfitOverTime was not identifiable, so I added the id on date.

In both the case of the years being identical and of the years being different the lines concatenate instead of overlay like in the screenshot on the docs page.

Is this method of multiple lines still available and I'm just doing it wrong?

Is only method to have multiple lines series as seen on the LineMark docs page?

struct ChartTests: View {
    struct ProfitOverTime {
        var date: Date
        var profit: Double
    }

    static func months(forYear year:Int) -> [Date] {
        var days:[Date] = []
        var dateComponets = DateComponents()
        //let year = Calendar.current.component(.year, from: Date())
        dateComponets.year = year
        dateComponets.day = 1
        for i in 1...12 {
            dateComponets.month = i
            if let date = Calendar.current.date(from: dateComponets) {
                days.append(date)
            }
        }
        return days
    }

    static func dataBuilder(forYear year:Int) -> [ProfitOverTime]{
        var data:[ProfitOverTime] = []
        for month in months(forYear: year) {
            let new = ProfitOverTime(date: month, profit: Double.random(in: 200...600))
            data.append(new)
        }
        return data
    }

    let departmentAProfit: [ProfitOverTime] = Self.dataBuilder(forYear: 2021)
    let departmentBProfit: [ProfitOverTime] = Self.dataBuilder(forYear: 2021)

    var body: some View {
        Chart {
            ForEach(departmentAProfit, id: \.date) {
                LineMark(
                    x: .value("Date", $0.date),
                    y: .value("Profit A", $0.profit)
                )
                .foregroundStyle(.blue)
            }

            ForEach(departmentBProfit, id: \.date) {
                LineMark(
                    x: .value("Date", $0.date),
                    y: .value("Profit B", $0.profit)
                )
                .foregroundStyle(.green)
            }
            RuleMark(
                y: .value("Threshold", 500.0)
            )
            .foregroundStyle(.red)
        }
    }
}

struct ChartTests_Previews: PreviewProvider {
    static var previews: some View {
        ChartTests()
    }
}

Add a Comment

Accepted Reply

Your LineMark is missing series. I think if you put that in it will break it into multiple plots like you want.

  • Yes. The original code did not have the series in it, but I'm glad to see that they updated the code! Thank you for the heads up. Also glad that the solution does not require you to generate a dataset with an extra column for series information like the LineMark example shows. That was what I was hoping to avoid. I'll post the working code in a seperate reply for posterity. Thanks for taking a look.

Add a Comment

Replies

Your LineMark is missing series. I think if you put that in it will break it into multiple plots like you want.

  • Yes. The original code did not have the series in it, but I'm glad to see that they updated the code! Thank you for the heads up. Also glad that the solution does not require you to generate a dataset with an extra column for series information like the LineMark example shows. That was what I was hoping to avoid. I'll post the working code in a seperate reply for posterity. Thanks for taking a look.

Add a Comment

Example code that works from the updated page

(I should add that this code leaves in the fact that I made ProfitOverTime conform to Identifiable and the new code on the documentation page uses ForEach(departmentAProfit, id: \.date) instead.)

import SwiftUI
import Charts

struct ChartTests: View {
    struct ProfitOverTime:Identifiable {
        var date: Date
        var profit: Double
        var id = UUID()
    }

    static func months(forYear year:Int) -> [Date] {
        var days:[Date] = []
        var dateComponets = DateComponents()
        //let year = Calendar.current.component(.year, from: Date())
        dateComponets.year = year
        dateComponets.day = 1
        for i in 1...12 {
            dateComponets.month = i
            if let date = Calendar.current.date(from: dateComponets) {
                days.append(date)
            }
        }
        return days
    }

    static func dataBuilder(forYear year:Int) -> [ProfitOverTime]{
        var data:[ProfitOverTime] = []
        for month in months(forYear: year) {
            let new = ProfitOverTime(date: month, profit: Double.random(in: 200...600))
            data.append(new)
        }
        return data
    }

    let departmentAProfit: [ProfitOverTime] = Self.dataBuilder(forYear: 2021)
    let departmentBProfit: [ProfitOverTime] = Self.dataBuilder(forYear: 2021)

    var body: some View {
        Chart {
            ForEach(departmentAProfit) {
                LineMark(
                    x: .value("Date", $0.date),
                    y: .value("Profit A", $0.profit),
                    series: .value("Company", "A")
                )
                .foregroundStyle(.blue)
            }

            ForEach(departmentBProfit) {
                LineMark(
                    x: .value("Date", $0.date),
                    y: .value("Profit B", $0.profit),
                    series: .value("Company", "B")
                )
                .foregroundStyle(.green)
            }
            RuleMark(
                y: .value("Threshold", 500.0)
            )
            .foregroundStyle(.red)
        }
    }
}

struct ChartTests_Previews: PreviewProvider {
    static var previews: some View {
        ChartTests()
    }
}

Screenshot

: