diff --git a/packages/react-native/scripts/swiftpm/update-xcodeproject.js b/packages/react-native/scripts/swiftpm/update-xcodeproject.js index e5e11ebe846..4ccf816fabb 100644 --- a/packages/react-native/scripts/swiftpm/update-xcodeproject.js +++ b/packages/react-native/scripts/swiftpm/update-xcodeproject.js @@ -30,6 +30,80 @@ function convertXcodeProjectToJSON(projectPath) { return JSON.parse(jsonOutput); } +/** + * Remove all existing SwiftPM package references and dependencies from Xcode project + * @param {Object} xcodeProject - The xcode project converted in JSON format + */ +function deintegrateSwiftPM(xcodeProject) { + const objects = xcodeProject.objects; + const objectsToRemove = []; + + // Step 1: Find all PBXNativeTarget objects and clean up their SwiftPM dependencies + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa === "PBXNativeTarget") { + // Find PBXFrameworksBuildPhase + for (const buildPhaseId of object.buildPhases || []) { + const buildPhaseObject = objects[buildPhaseId]; + if (buildPhaseObject && buildPhaseObject.isa === "PBXFrameworksBuildPhase") { + const filesToRemove = []; + + // Check each file in the build phase + for (const fileId of buildPhaseObject.files || []) { + const buildFileObject = objects[fileId]; + if (buildFileObject && buildFileObject.isa === "PBXBuildFile" && buildFileObject.productRef) { + const productRefObject = objects[buildFileObject.productRef]; + if (productRefObject && productRefObject.isa === "XCSwiftPackageProductDependency") { + // Mark for removal: the product dependency, the build file, and remove from files list + objectsToRemove.push(buildFileObject.productRef); + objectsToRemove.push(fileId); + filesToRemove.push(fileId); + } + } + } + + // Remove files from the build phase + if (filesToRemove.length > 0) { + buildPhaseObject.files = (buildPhaseObject.files || []).filter(fileId => !filesToRemove.includes(fileId)); + } + } + } + } + } + + // Step 2: Find PBXProject and clean up packageReferences + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa === "PBXProject") { + const packageReferencesToRemove = []; + + // Check each package reference + for (const packageRefId of object.packageReferences || []) { + const packageRefObject = objects[packageRefId]; + if (packageRefObject && packageRefObject.isa === "XCLocalSwiftPackageReference") { + // Mark for removal + objectsToRemove.push(packageRefId); + packageReferencesToRemove.push(packageRefId); + } + } + + // Remove package references from the project + if (packageReferencesToRemove.length > 0) { + object.packageReferences = (object.packageReferences || []).filter(refId => !packageReferencesToRemove.includes(refId)); + } + + break; + } + } + + // Step 3: Remove all marked objects + for (const objectId of objectsToRemove) { + delete objects[objectId]; + } + + console.log(`✓ Removed ${objectsToRemove.length} SwiftPM-related objects from Xcode project`); +} + /** * Add local SwiftPM package references and product dependencies to Xcode project * @param {string} relativePath - The relative path of where the Package.swift is located @@ -116,6 +190,9 @@ function integrateSwiftPackagesInXcode(xcodeProjectPath, packageSwiftObjects, ap // Convert to JSON const xcodeProject = convertXcodeProjectToJSON(projectPbxprojPath); + // Remove any existing SwiftPM integrations first + deintegrateSwiftPM(xcodeProject); + // Iterate over PackageSwift objects and execute addLocalSwiftPM for (const packageSwift of packageSwiftObjects) { addLocalSwiftPM( @@ -157,6 +234,7 @@ if (require.main === module) { module.exports = { generateXcodeObjectId, convertXcodeProjectToJSON, + deintegrateSwiftPM, addLocalSwiftPM, integrateSwiftPackagesInXcode };