Skip to content

fix(iOS): prevent UUID collision corrupting Pods project in spm_dependency#57576

Closed
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-dependency-uuid-collision
Closed

fix(iOS): prevent UUID collision corrupting Pods project in spm_dependency#57576
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-dependency-uuid-collision

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary:

The first nightly containing the prebuilt/SwiftPM stack (0.88.0-nightly-20260716) broke the react-native-enriched-markdown job in react-native-community/nightly-tests: pod install succeeds, but xcodebuild cannot load the generated project:

-[XCRemoteSwiftPackageReference _setSavedArchiveVersion:]: unrecognized selector sent to instance
Failed to load container at path: .../Pods/Pods.xcodeproj — "The project 'Pods' is damaged and cannot be opened."

Root cause: spm_dependency injects XCRemoteSwiftPackageReference / XCSwiftPackageProductDependency objects from a post_install hook via project.new(...). CocoaPods' Pod::Project overrides UUID generation with a deterministic counter scheme (prefix + %07X counter + trailing 0) and deliberately skips collision checks, assuming the project is always freshly generated. When the generator counters are out of sync with the loaded object graph, the first generated UUID collides with an existing object — in the observed failure the root PBXProject's own UUID (46EB2E00000000). The injected package reference silently overwrites the root object entry in objects_by_uuid, so the saved pbxproj's rootObject points at the package reference, and Xcode's loader sends PBXProject-only messages to it. The bug is latent in spm_dependency since its introduction; the recently grown Pods object graph merely exposed it.

Fix: route object creation through a new_object helper that keeps the deterministic counter scheme but probes generate_uuid forward past any UUID already present in objects_by_uuid. Uniqueness is guaranteed against the live object table regardless of project size/state; generate_uuid's counter is strictly monotonic, so the probe terminates and never repeats within a run. Repeated pod installs stay idempotent via the existing find-before-create dedup guards.

Changelog:

[IOS] [FIXED] - Fix "The project 'Pods' is damaged and cannot be opened" when a library uses spm_dependency and the generated UUID collides with an existing Pods project object

Test Plan:

  • New standalone test scripts/cocoapods/__tests__/spm-test.rb (runs via ruby scripts/cocoapods/__tests__/spm-test.rb, uses the real Pod::Project):
    • fresh-project injection (passes before and after — fresh counters don't collide, isolating the trigger),
    • injection after counter reset (reproduces the corruption against the previous code as NoMethodError: undefined method 'product_ref_group' for ... XCRemoteSwiftPackageReference on reload — the Ruby analog of Xcode's crash; passes with the fix),
    • global UUID uniqueness + clean Xcodeproj::Project.open round-trip.
  • 3 tests, 10 assertions, 0 failures with the fix.
  • E2E: to be confirmed with a spm_dependency library (e.g. react-native-enriched-markdown) in prebuilt mode.

🤖 Generated with Claude Code

…dency

spm_dependency injects XCRemoteSwiftPackageReference /
XCSwiftPackageProductDependency objects into the Pods project from a
post_install hook via project.new(...). CocoaPods' Pod::Project
overrides UUID generation with a deterministic counter scheme
(prefix + %07X counter + trailing 0) and deliberately skips collision
checks, assuming the project is always freshly generated. When the
generator counters are out of sync with the loaded object graph, the
first generated UUID can collide with an existing object — in the
observed failure, the root PBXProject's own UUID (46EB2E00000000). The
injected package reference then silently overwrites the root object
entry, and the saved pbxproj's rootObject points at the package
reference. Xcode fails to load the project:

  -[XCRemoteSwiftPackageReference _setSavedArchiveVersion:]:
  unrecognized selector
  "The project 'Pods' is damaged and cannot be opened."

This broke the react-native-enriched-markdown job in nightly-tests on
0.88.0-nightly-20260716 after the Pods graph grew with the prebuilt
pods; the bug itself is latent and independent of that change.

Fix: route object creation through a new_object helper that keeps the
deterministic counter scheme but probes generate_uuid forward past any
UUID already present in objects_by_uuid. Uniqueness is guaranteed
against the live object table regardless of project size or state, and
repeated pod installs remain stable via the existing find-before-create
dedup guards.

Adds a standalone test (scripts/cocoapods/__tests__/spm-test.rb)
covering fresh-project injection, the reload/collision regression, and
global UUID uniqueness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 16, 2026
@chrfalch
chrfalch requested a review from cipolleschi July 16, 2026 11:34

@cipolleschi cipolleschi 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.

Thanks for the fix

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 16, 2026
@meta-codesync

meta-codesync Bot commented Jul 16, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D112320831.

@meta-codesync meta-codesync Bot closed this in 1cdf784 Jul 16, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Jul 16, 2026
@meta-codesync

meta-codesync Bot commented Jul 16, 2026

Copy link
Copy Markdown

@cipolleschi merged this pull request in 1cdf784.

react-native-bot pushed a commit that referenced this pull request Jul 16, 2026
…dency (#57576)

Summary:
The first nightly containing the prebuilt/SwiftPM stack (`0.88.0-nightly-20260716`) broke the `react-native-enriched-markdown` job in [react-native-community/nightly-tests](https://github.com/react-native-community/nightly-tests/actions/runs/29476835418/job/87551590732): `pod install` succeeds, but xcodebuild cannot load the generated project:

```
-[XCRemoteSwiftPackageReference _setSavedArchiveVersion:]: unrecognized selector sent to instance
Failed to load container at path: .../Pods/Pods.xcodeproj — "The project 'Pods' is damaged and cannot be opened."
```

**Root cause:** `spm_dependency` injects `XCRemoteSwiftPackageReference` / `XCSwiftPackageProductDependency` objects from a `post_install` hook via `project.new(...)`. CocoaPods' `Pod::Project` overrides UUID generation with a deterministic counter scheme (`prefix + %07X counter + trailing 0`) and deliberately skips collision checks, assuming the project is always freshly generated. When the generator counters are out of sync with the loaded object graph, the first generated UUID collides with an existing object — in the observed failure the root `PBXProject`'s own UUID (`46EB2E00000000`). The injected package reference silently overwrites the root object entry in `objects_by_uuid`, so the saved pbxproj's `rootObject` points at the package reference, and Xcode's loader sends `PBXProject`-only messages to it. The bug is latent in `spm_dependency` since its introduction; the recently grown Pods object graph merely exposed it.

**Fix:** route object creation through a `new_object` helper that keeps the deterministic counter scheme but probes `generate_uuid` forward past any UUID already present in `objects_by_uuid`. Uniqueness is guaranteed against the live object table regardless of project size/state; `generate_uuid`'s counter is strictly monotonic, so the probe terminates and never repeats within a run. Repeated `pod install`s stay idempotent via the existing find-before-create dedup guards.

## Changelog:

[IOS] [FIXED] - Fix "The project 'Pods' is damaged and cannot be opened" when a library uses `spm_dependency` and the generated UUID collides with an existing Pods project object

Pull Request resolved: #57576

Test Plan:
- New standalone test `scripts/cocoapods/__tests__/spm-test.rb` (runs via `ruby scripts/cocoapods/__tests__/spm-test.rb`, uses the real `Pod::Project`):
  - fresh-project injection (passes before and after — fresh counters don't collide, isolating the trigger),
  - injection after counter reset (reproduces the corruption against the previous code as `NoMethodError: undefined method 'product_ref_group' for ... XCRemoteSwiftPackageReference` on reload — the Ruby analog of Xcode's crash; passes with the fix),
  - global UUID uniqueness + clean `Xcodeproj::Project.open` round-trip.
- 3 tests, 10 assertions, 0 failures with the fix.
- E2E: to be confirmed with a `spm_dependency` library (e.g. react-native-enriched-markdown) in prebuilt mode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed By: fabriziocucci

Differential Revision: D112320831

Pulled By: cipolleschi

fbshipit-source-id: de190f6492abff2cc6dbd94347e5346d11b50a41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants