PC パソコン

Xcode「the compiler is unable to type-check this expression in reasonable time」の解決方法【Swift Charts・Xcode 27】

Xcode 27でSwift Chartsをビルドすると、以前は通っていたChart内の長い条件分岐に「the compiler is unable to type-check this expression in reasonable time」が表示されることがあります。Appleは、OS 27未満へのバックデプロイ用互換処理と、多数の分岐が組み合わさった型検査上限の問題として説明しています。

表示されるエラー

error: the compiler is unable to type-check this expression in reasonable time;
try breaking up the expression into distinct sub-expressions

典型例は、選択された指標に応じてChart内で多数のLineMarkを切り替えるコードです。

import SwiftUI
import Charts

struct MetricChartView: View {
    let selectedMetric: String
    let points: [DataPoint]

    var body: some View {
        Chart(points) { point in
            if selectedMetric == "Rate" {
                LineMark(x: .value("X", point.index),
                         y: .value("Y", point.rate))
            } else if selectedMetric == "Signal" {
                LineMark(x: .value("X", point.index),
                         y: .value("Y", point.signal))
            }
            // 同様の分岐が多数続く
        }
    }
}

原因

Xcode 27のSwiftUIでは、複数の結果ビルダーが統合された@ContentBuilderが使われます。最小デプロイメントターゲットがOS 27未満のSwift Chartsでは、条件分岐を互換動作させるため、Charts固有のBuilderConditionalを返すbuildEitherオーバーロードが追加されます。

その候補と深いif / else ifやswitchが組み合わさると、コンパイラが検討する組み合わせが急増し、型検査の複雑さ上限を超えます。

Appleが示す発生目安は約10個以上の分岐です。式の構造でも変わるため、10個未満なら必ず発生しないという意味ではありません。

発生条件・対象バージョン

  • Xcode 27でSwiftUIとSwift Chartsを使用している
  • 最小デプロイメントターゲットがiOS 27、iPadOS 27、macOS 27、visionOS 27より前
  • Chartクロージャ内に深い条件分岐がある
  • 分岐ごとに異なるチャートコンテンツを返している

Appleによれば、最小ターゲットが各OS 27以降なら互換オーバーロードが組み込まれず、この固有の問題は発生しません。修正版Xcodeの指定はなく、公式対応はソースの分割です。

推奨する解決手順

分岐を@ChartContentBuilder関数へ抽出する

import SwiftUI
import Charts

struct MetricChartView: View {
    let selectedMetric: String
    let points: [DataPoint]

    var body: some View {
        Chart(points) { point in
            marks(for: point)
        }
    }

    @ChartContentBuilder
    private func marks(for point: DataPoint) -> some ChartContent {
        if selectedMetric == "Rate" {
            LineMark(x: .value("X", point.index),
                     y: .value("Y", point.rate))
                .foregroundStyle(.blue)
        } else if selectedMetric == "Signal" {
            LineMark(x: .value("X", point.index),
                     y: .value("Y", point.signal))
                .foregroundStyle(.green)
        } else if selectedMetric == "Noise" {
            LineMark(x: .value("X", point.index),
                     y: .value("Y", point.noise))
                .foregroundStyle(.red)
        } else {
            LineMark(x: .value("X", point.index),
                     y: .value("Y", point.defaultValue))
                .foregroundStyle(.gray)
        }
    }
}

これはApple推奨の方法です。分岐の型検査を専用関数へ隔離し、外側のChart式をコンパイラの処理上限内に収めます。

代替策

分岐をさらに小さな関数へ分割する

抽出後も分岐が非常に多い場合は、指標の種類ごとに複数の@ChartContentBuilder関数へ分けます。

値の選択を通常関数へ移す

private func value(for point: DataPoint) -> Double {
    switch selectedMetric {
    case "Rate": return point.rate
    case "Signal": return point.signal
    case "Noise": return point.noise
    default: return point.defaultValue
    }
}

var body: some View {
    Chart(points) { point in
        LineMark(x: .value("X", point.index),
                 y: .value("Y", value(for: point)))
    }
}

全分岐が同じMarkを作るなら、値だけを通常のSwift関数で選ぶとビルダー内の型分岐を減らせます。

最小デプロイメントターゲットを27へ上げる

旧OSをサポートする必要がない場合のみ選択できます。対応端末を失うため、エラー回避だけを目的に引き上げる方法は推奨しません。

確認方法

  1. 長い分岐を@ChartContentBuilder関数へ抽出します。
  2. 最小ターゲットを変えず、Xcode 27でClean Build Folderを実行して再ビルドします。
  3. 診断がビルドログから消えたことを確認します。
  4. サポート対象の旧OSシミュレータまたは実機で全分岐を切り替えます。
  5. Release構成と条件付きコンパイルの経路も確認します。

注意点

  • Derived Dataの削除だけでは候補数は減りません。
  • AnyViewなど無関係な型消去を追加する前に、Charts専用ビルダーで分割してください。
  • 同じ診断文は巨大なSwift式全般で表示されます。本記事はXcode 27、Swift Charts、OS 27未満へのバックデプロイ、多数の分岐が揃うケースが対象です。
  • 公式に確認できない将来の修正バージョンを前提にせず、現在のApple推奨策を適用してください。

まとめ

このエラーは、バックデプロイ用のSwift Charts互換オーバーロードと深い条件分岐により、型検査候補が急増して起きます。分岐を@ChartContentBuilder付き関数へ抽出するのが第一の解決策です。

参照した公式情報