Skip to content

feat: add traits to XCRemoteSwiftPackageReference and XCLocalSwiftPackageReference#1114

Merged
pepicrft merged 7 commits into
tuist:mainfrom
chigichan24:support-package-traits
Jul 13, 2026
Merged

feat: add traits to XCRemoteSwiftPackageReference and XCLocalSwiftPackageReference#1114
pepicrft merged 7 commits into
tuist:mainfrom
chigichan24:support-package-traits

Conversation

@chigichan24

@chigichan24 chigichan24 commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

What changed

This pull request adds optional traits: [String]? support to both XCRemoteSwiftPackageReference and XCLocalSwiftPackageReference.

XcodeProj now:

  • Decodes package trait selections from Xcode project files.
  • Writes populated and explicitly empty trait arrays without collapsing their meaning.
  • Includes traits in package-reference equality.
  • Preserves trait selections through a project read and write cycle.
  • Correctly compares local package references by relative path.
  • Keeps the optimized string escaping path on supported systems and uses a compatible fallback on older Apple operating system versions.

Why

Xcode 26.4 writes Swift Package Manager package traits on package-reference objects:

traits = (
    NoUIFramework,
);

Without model support, reading and writing one of these projects silently removes the trait selection. Xcode then falls back to the package defaults the next time the project opens.

The branch also needed to remain buildable for the package's existing iOS 12 deployment target after being updated to current main.

Root cause

The package-reference models did not decode, store, serialize, or compare the traits field introduced by Xcode.

While adding equality coverage, the implementation also exposed an older generated-equality gap for XCLocalSwiftPackageReference. Its relative path was not part of equality, so local references with different paths could compare as equal.

Separately, current main uses a string initializer introduced in iOS 14 from code compiled for iOS 12. This only surfaced after the pull request workflows moved to the latest Xcode runner.

Approach

Traits use [String]? to preserve the project file exactly:

  • nil means no explicit selection, so package defaults apply.
  • [] means default traits are explicitly disabled.
  • A populated array contains the explicitly enabled traits.

An array is used instead of a set because Xcode project arrays preserve source-file order.

The selection remains on the local or remote package reference. Product dependency objects are unchanged because Xcode does not write traits there.

A dedicated fixture covers a populated local selection and an explicitly empty remote selection. The integration test verifies that reading and writing the fixture produces no diff.

String escaping keeps the direct-storage implementation where that initializer is available. Older supported systems use the same escaping routine through a byte buffer.

Impact

Projects using package traits can now be safely read, modified, and written with XcodeProj without losing their trait configuration. Existing callers remain source compatible because the new initializer arguments default to nil.

The package also continues to compile for its existing iOS 12 deployment target.

Validation

  • mise run lint
  • mise run test: 381 XCTest tests and 6 Swift Testing tests passed.
  • xcodebuild build -scheme XcodeProj -destination 'generic/platform=iOS Simulator'
  • xcodebuild test -scheme XcodeProj on an iPhone 17 Pro simulator running iOS 26.5: 323 XCTest tests and 6 Swift Testing tests passed, with 5 expected skips.
  • The package-traits fixture read and write test passed without a diff.

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Apr 19, 2026

@pepicrft pepicrft left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated against current main. The implementation preserves absent, empty, and populated package trait selections, includes traits in equality, and keeps project read and write operations stable. The full test suite and repository lint command pass.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 13, 2026

@pepicrft pepicrft left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revalidated at the current head. The package traits model and project read and write coverage are sound. The availability fallback preserves iOS 12 build compatibility while retaining the optimized path on newer systems. All repository workflows pass.

@pepicrft pepicrft merged commit 9b7b9d5 into tuist:main Jul 13, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants