From 561bd988c54dc151faeace5a4e5a05566b5ef494 Mon Sep 17 00:00:00 2001 From: Kosikowski <8352262+Kosikowski@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:16:13 +0000 Subject: [PATCH 1/2] Add delete support for XCLocalSwiftPackageReference The delete method in PBXObjects was missing support for XCLocalSwiftPackageReference, causing local package references to remain as orphaned objects in the pbxproj when switching from local back to remote packages. This adds handling for localSwiftPackageReferences in the delete method, matching the existing pattern for remoteSwiftPackageReferences. --- Sources/XcodeProj/Objects/Project/PBXObjects.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/XcodeProj/Objects/Project/PBXObjects.swift b/Sources/XcodeProj/Objects/Project/PBXObjects.swift index 70083a779..67388444b 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjects.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjects.swift @@ -298,6 +298,8 @@ class PBXObjects: Equatable { return _buildRules.remove(at: index).value } else if let index = remoteSwiftPackageReferences.index(forKey: reference) { return _remoteSwiftPackageReferences.remove(at: index).value + } else if let index = localSwiftPackageReferences.index(forKey: reference) { + return _localSwiftPackageReferences.remove(at: index).value } else if let index = swiftPackageProductDependencies.index(forKey: reference) { return _swiftPackageProductDependencies.remove(at: index).value } else if let index = fileSystemSynchronizedRootGroups.index(forKey: reference) { From 0fd735ac8753ff44a9388bbd85a795b914c44043 Mon Sep 17 00:00:00 2001 From: Kosikowski <8352262+Kosikowski@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:24:10 +0000 Subject: [PATCH 2/2] Add tests for XCLocalSwiftPackageReference add/delete operations Tests verify that: - Adding a local package reference to PBXProj works correctly - Deleting a local package reference removes it from PBXProj - Deleting one of multiple local packages removes only the correct one - Deleting an object not in the project is a no-op --- .../XCLocalSwiftPackageReferenceTests.swift | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift b/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift index 17d28f42e..1ad9d94f4 100644 --- a/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift +++ b/Tests/XcodeProjTests/Objects/SwiftPackage/XCLocalSwiftPackageReferenceTests.swift @@ -51,4 +51,74 @@ final class XCLocalSwiftPackageReferenceTests: XCTestCase { // Then XCTAssertEqual(subject.name, "tuist/xcodeproj") } + + // MARK: - Add/Delete Tests + + func test_add_addsObjectToPBXProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage = XCLocalSwiftPackageReference(relativePath: "../MyPackage") + + // When + proj.add(object: localPackage) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + XCTAssertEqual(proj.objects.localSwiftPackageReferences.values.first?.relativePath, "../MyPackage") + } + + func test_delete_removesObjectFromPBXProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage = XCLocalSwiftPackageReference(relativePath: "../MyPackage") + proj.add(object: localPackage) + + // Verify it was added + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + + // When + proj.delete(object: localPackage) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 0) + } + + func test_delete_removesCorrectObject_whenMultipleExist() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let localPackage1 = XCLocalSwiftPackageReference(relativePath: "../Package1") + let localPackage2 = XCLocalSwiftPackageReference(relativePath: "../Package2") + let localPackage3 = XCLocalSwiftPackageReference(relativePath: "../Package3") + proj.add(object: localPackage1) + proj.add(object: localPackage2) + proj.add(object: localPackage3) + + // Verify all were added + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 3) + + // When - delete the middle one + proj.delete(object: localPackage2) + + // Then + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 2) + let remainingPaths = proj.objects.localSwiftPackageReferences.values.map(\.relativePath) + XCTAssertTrue(remainingPaths.contains("../Package1")) + XCTAssertTrue(remainingPaths.contains("../Package3")) + XCTAssertFalse(remainingPaths.contains("../Package2")) + } + + func test_delete_doesNothing_whenObjectNotInProj() { + // Given + let proj = PBXProj.fixture(rootObject: nil, objects: []) + let addedPackage = XCLocalSwiftPackageReference(relativePath: "../AddedPackage") + let notAddedPackage = XCLocalSwiftPackageReference(relativePath: "../NotAddedPackage") + proj.add(object: addedPackage) + + // When - try to delete an object that was never added + proj.delete(object: notAddedPackage) + + // Then - the added package should still be there + XCTAssertEqual(proj.objects.localSwiftPackageReferences.count, 1) + XCTAssertEqual(proj.objects.localSwiftPackageReferences.values.first?.relativePath, "../AddedPackage") + } }