swiftswiftuiswiftui-charts

How can we avoid build errors when using Vectorized Chart Content (eg LinePlot and PointPlot) in Swift Charts?


I'm curious, has anyone else run into compile problems when using the new Vectorized Chart Content APIs that Apple introduced in iOS 18?

I find it incredibly easy to get build errors like "Compiling failed: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions", and I'm wondering if I'm doing something wrong or if there is a common workaround.

For example, consider this code, which is not that complicated, and not too dissimilar to some functional code resulting from a previous question:

private struct PlotPoint {
    let x: Double
    let y: Double
}

// We build a dictionary of the form "Series X": [ (x1, y1), ... ]
private let data: [String : [PlotPoint]] = Dictionary(
    uniqueKeysWithValues: Array(0...7).map { series in
        (
            // Dict key followed by Dict value
            "Series \(series + 1)",
            stride(from: 0.0, through: 1.0, by: 0.05).map { y in
                PlotPoint(x: y*y, y: y*Double(series + 1)/8.0)
            }
        )
    }
)

struct ChartWithBothStyleAndSymbol: View {
    var body: some View {
        Chart {
            ForEach(Array(data.keys).sorted(), id: \.self) { key in
                LinePlot(
                    data[key] ?? [],
                    x: .value("x", \.x),
                    y: .value("y", \.y)
                )
//                .symbol(by: .value("Series", key))
                .foregroundStyle(by: .value("Series", key))
            }
        }
        .padding()
    }
}

Working Chart without Symbols Applied

Here we have a LinePlots that has foregroundStyle(by:) applied to it. It works fine. But if I uncomment the line that applies symbol(by:), then the compiler can't cope.

If I replace the LinePlot with a PointPlot, things become even more brittle. I have to remove both foregroundStyle(by:) and symbol(by:) to get a successful compile. What can I do? How can I break up the expression into distinct sub-expressions, as suggested by the error?


Solution

  • This error often appears when there is a type mismatch somewhere in your code. But in this case, there is nothing wrong with your code. The Swift compiler is just too slow. On my M4 MacBook Pro, your code actually compiles successfully, but if I add more .foregroundStyle(by:) calls, it eventually fails to compile.

    Compared to the non-vectorised chart content, the vectorised chart content APIs are more strongly typed, and therefore the compiler needs to do more type-checking.

    One possible bottleneck are the .value(...) calls. The compiler needs to infer the two type parameters of the PlottableProjection. You can somewhat speed up the compilation by specifying the type parameters yourself:

    .symbol(by: PlottableProjection<PlotPoint, String>.value("Series", key))
    

    The first parameter is the type of the data you are plotting, and the second parameter is the type of key.

    The most effective way to make the compilation faster, is of course to break it up into multiple expressions (but in turn make the code more ugly). You can do it like this:

    let plot = LinePlot(
        data[key] ?? [],
        x: .value("x", \.x),
        y: .value("y", \.y)
    )
    let plotWithSymbol = plot.symbol(by: PlottableProjection<PlotPoint, String>.value("Series", key))
    plotWithSymbol.foregroundStyle(by: .value("Series", key))