Skip to content

fix(cli): support Xcode's JSON project format (project.xcproj) - #8608

Open
erkamyaman wants to merge 1 commit into
ionic-team:mainfrom
erkamyaman:main
Open

erkamyaman wants to merge 1 commit into
ionic-team:mainfrom
erkamyaman:main

Conversation

@erkamyaman

@erkamyaman erkamyaman commented Sep 16, 2026

Copy link
Copy Markdown

Fixes #8607

Xcode 27.2 can store a project as App.xcodeproj/project.xcproj (JSON with trailing commas) instead of project.pbxproj. The CLI read project.pbxproj unconditionally, so after converting a project, cap sync ios failed to rewrite CapApp-SPM/Package.swift while still exiting 0. Added and removed plugins then never reached iOS, and removing one broke the Xcode build.

Changes

  • getXcodeProjectFile() returns project.pbxproj, or project.xcproj when that's the only file present. Projects that have project.pbxproj take exactly the same code paths as before.
  • For project.xcproj, build settings are matched as "KEY": "value", including per-configuration keys such as "IPHONEOS_DEPLOYMENT_TARGET[config=Release]", which Xcode writes when Debug and Release differ.
    • getMajoriOSVersion() (used when generating Package.swift and by cap update and cap migrate) returns the lowest deployment target in the file.
    • cap migrate sets every deployment target through the new setXcprojDeploymentTarget().
    • editProjectSettingsIOS() sets PRODUCT_BUNDLE_IDENTIFIER. Each file format only gets its own pattern.
  • The UIScene migration can't register SceneDelegate.swift in a project.xcproj, because the xcode package only parses .pbxproj. It now says so and asks for the file to be added in Xcode, or skips if the file is already listed.

Not covered

  • The underlying exit code: a failure while writing Package.swift is still logged without failing cap sync. This PR removes the cause for project.xcproj projects but doesn't change that error handling.
  • determineIOSWebDirAbs() still only looks at project.pbxproj, which only matters for the deprecated layout with public at the iOS project root.
  • CocoaPods can't read project.xcproj, so pod install fails on converted CocoaPods projects. That's outside the CLI.
  • next has the same code, so this will need to reach it too.

Testing

  • New cli/test/ios-project-file.spec.ts. Its two fixtures were generated from ios-spm-template with xcodebuild -convert-project "Xcode Project" (Xcode 27.2 beta, 27B5019j): one as shipped, and one with a 16.0 Release deployment target, which produces the per-configuration keys. The tests cover choosing the file, reading the deployment target (including per configuration), setting every deployment target, and setting the bundle ID in both formats while leaving the rest of project.xcproj byte-identical.
  • npm test in cli: 13 suites, 92 tests pass. ESLint and Prettier pass on the changed files.
  • End to end, with blank Capacitor 8.5.2 apps and Xcode 27.2 beta:
    • A regular project (project.pbxproj): cap sync ios and cap update ios produce byte-identical ios/ output with this change and with 8.5.2.
    • A converted project: removing @capacitor/app and syncing drops it from Package.swift, and adding it back restores it. Both syncs run without errors, and the app builds after each.
    • A converted project with a 15.0 Debug and 16.0 Release deployment target: cap sync ios writes platforms: [.iOS(.v15)], and the app builds.
    • cap migrate --packagemanager npm on that project, after setting the targets to 14.0 and 13.0: all four deployment target keys become 15.0, and the app builds.

@erkamyaman
erkamyaman marked this pull request as draft September 16, 2026 22:43
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.

[Bug]: cap sync ios doesn't support Xcode 27.2's JSON project format (project.xcproj)

2 participants