Skip to content

Fix Xcode 27 "switch must be exhaustive" build error#7

Open
maipeera wants to merge 2 commits into
dmakwt:mainfrom
maipeera:fix/xcode27-exhaustive-switch
Open

Fix Xcode 27 "switch must be exhaustive" build error#7
maipeera wants to merge 2 commits into
dmakwt:mainfrom
maipeera:fix/xcode27-exhaustive-switch

Conversation

@maipeera

@maipeera maipeera commented Jun 24, 2026

Copy link
Copy Markdown

Problem

Building the plugin with Xcode 27 fails with:

error: switch must be exhaustive
note: add missing case: '.attachment(_)'
note: add missing case: '.custom(_)'
note: add missing case: '.reasoning(_)'
note: handle unknown values using "@unknown default"

The Xcode 27 SDK adds new cases to several non-frozen Foundation Models enums:

Enum New case(s) in iOS/macOS 27 SDK
Transcript.Segment .attachment, .custom
Transcript.Entry .reasoning

Because these enums are not @frozen, the previously-exhaustive switch statements in serializeSegment(_:) and mapTranscriptEntries(_:) no longer cover every case — a hard compile error under the newer SDK. Apps depending on this plugin can no longer build once they move to Xcode 27.

Fix

Two layers, so the plugin builds everywhere and makes use of the new data where available:

1. Compile fix (works on every toolchain). Added @unknown default to every switch over a non-frozen Apple framework enum:

  • mapTranscriptEntriesswitch entry (Transcript.Entry)
  • serializeSegmentswitch segment (Transcript.Segment)
  • sanitizeGenerationErrorswitch generationError (LanguageModelSession.GenerationError), added defensively for the same class of issue

This matches the pattern already used in this file for SystemLanguageModel.Availability.UnavailableReason and is Apple's recommended approach for non-frozen library enums.

2. First-class handling on Xcode 27+. The new cases are serialized explicitly, gated behind #if compiler(>=6.4) (Swift 6.4 ships with Xcode 27):

  • Transcript.Entry.reasoning → dedicated "reasoning" role, segments mapped like any other entry
  • Transcript.Segment.attachment / .custom → serialized explicitly

The gate keeps the plugin building on Xcode 26, where these cases don't exist in the SDK — there it simply falls back to the @unknown default branch. @unknown default is retained on every switch because the enums remain non-frozen (future SDKs will add more cases).

Switches intentionally left unchanged:

  • switch model.availabilitySystemLanguageModel.Availability is @frozen, so @unknown default is neither allowed nor needed.
  • switch levelGuardrailLevel is a local enum defined in this package.

Applied identically to both the iOS and macOS plugin sources.

Verification

Against the real iOS 27 SDK (iPhoneOS27.0.sdk, Xcode 27.0, Swift 6.4):

  • Before: original serializeSegment switch reproduces error: switch must be exhaustive (typecheck exits non-zero).
  • After, gate ON (Xcode 27 path): the actual edited mapTranscriptEntries + serializeSegment bodies typecheck with 0 diagnostics, including binding .reasoning(let reasoning) and accessing reasoning.segments — no if #available needed.
  • After, gate OFF (simulating Xcode 26): 0 errors (the fallback references no new symbols).
  • A full xcodebuild of the example app's plugin sources compiles with zero errors.

Compatibility note

The #if compiler(>=6.4) gate keys off the Swift toolchain version as a proxy for "does this SDK contain the new cases." Apple ships each major SDK together with a matching Swift minor bump (Xcode 26 = Swift 6.2, Xcode 27 = Swift 6.4), so this is reliable in practice; Swift has no per-symbol "does this case exist" check.

Changes

  • ios/Classes/FoundationModelsFrameworkPlugin.swift@unknown default + compiler-gated explicit cases
  • macos/Classes/FoundationModelsFrameworkPlugin.swift — same
  • CHANGELOG.md0.2.2 entry
  • pubspec.yaml — bump version 0.2.10.2.2

🤖 Generated with Claude Code

maipeera and others added 2 commits June 24, 2026 18:56
The Xcode 27 SDK adds new cases to non-frozen Foundation Models enums:
- Transcript.Entry gains `.reasoning`
- Transcript.Segment gains `.attachment` and `.custom`

These broke the exhaustive switch statements in mapTranscriptEntries and
serializeSegment, causing a hard "switch must be exhaustive" compile error.

Add `@unknown default` clauses to all switches over non-frozen Apple
framework enums (Transcript.Entry, Transcript.Segment, and
LanguageModelSession.GenerationError) so the plugin keeps compiling as
Apple adds enum cases in future SDKs. Applied to both the iOS and macOS
plugin sources. Bump version to 0.2.2 and update CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Layer explicit handling of the new non-frozen enum cases on top of the
`@unknown default` compile fix, gated behind `#if compiler(>=6.4)`:

- Transcript.Entry `.reasoning` -> serialized with a dedicated "reasoning"
  role and its segments mapped like other entries.
- Transcript.Segment `.attachment` / `.custom` -> serialized explicitly.

The `#if compiler(>=6.4)` gate (Swift 6.4 ships with Xcode 27) keeps the
plugin building on Xcode 26, where these cases do not exist in the SDK;
older toolchains fall back to the `@unknown default` branch. `@unknown
default` is retained on all switches since the enums remain non-frozen.

Verified against the iOS 27 SDK: gate-on path typechecks with zero
diagnostics; gate-off path has zero errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant