Skip to content

fix: serialize schemes the way Xcode does - #1189

Open
stefanceriu wants to merge 1 commit into
tuist:mainfrom
stefanceriu:stefan/matchXcodeSchemeSerialization
Open

stefanceriu wants to merge 1 commit into
tuist:mainfrom
stefanceriu:stefan/matchXcodeSchemeSerialization

Conversation

@stefanceriu

Copy link
Copy Markdown

Short description 📝

Schemes written by XcodeProj (and so by XcodeGen) differ slightly from the way Xcode serializes the same scheme. As soon as Xcode touches a generated scheme (running tests, changing the selected test plan, opening the scheme editor) it rewrites the file, which shows up as recurring noise in projects that commit their generated schemes.

For example, for a scheme with a test plan Xcode:

  • moves TestPlans to the end of TestAction,
  • drops the empty Testables element,
  • drops empty CommandLineArguments elements,
  • drops runPostActionsOnFailure = "NO",
  • writes the TestPlanReference attributes as reference then default.

Solution 📦

Match Xcode's output for each of these cases:

  • Write TestPlans as the last child of TestAction.
  • Omit an empty Testables element, but only when the scheme uses test plans. Schemes written by Xcode keep an empty Testables when there are no test plans (see the existing Xcode-authored fixtures), so that case is unchanged.
  • Omit CommandLineArguments in the test, launch and profile actions when there are no arguments.
  • Only write runPostActionsOnFailure when it is enabled.
  • Add TestPlanReference to attributesOrder so its attributes are written as reference, default.

To verify, I built XcodeGen 2.46.0 against XcodeProj 9.14.0 with this patch applied and regenerated a real project (element-x-ios). The regenerated scheme is byte-identical to the copy Xcode 27 had saved.

Projects that commit generated schemes will get a one-off diff the first time they regenerate with this change.

Implementation 👩‍💻👨‍💻

  • Reorder and conditionally omit elements in XCScheme.TestAction.xmlElement().
  • Omit empty CommandLineArguments in LaunchAction and ProfileAction.
  • Only write runPostActionsOnFailure when true in BuildAction.
  • Add the TestPlanReference attribute order.
  • Update the iOS.xcscheme fixture to the new TestPlans position.
  • Add tests for omitting the empty elements, keeping Testables without test plans, and runPostActionsOnFailure.

Xcode rewrites schemes generated by XcodeProj as soon as it touches
them, which shows up as noise in projects that commit generated
schemes. Match Xcode's own output:

- Write `TestPlans` as the last child of `TestAction`.
- Omit an empty `Testables` element when the scheme uses test plans.
- Omit empty `CommandLineArguments` elements.
- Only write `runPostActionsOnFailure` when it is enabled.
- Write `TestPlanReference` attributes in Xcode's order (`reference`,
  then `default`).

Verified against a scheme re-saved by Xcode 27: regenerating it with
these changes produces a byte-identical file.
@stefanceriu
stefanceriu marked this pull request as ready for review September 30, 2026 12:38

This branch has not been deployed

No deployments
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