#8607·capacitor

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

Author: erkamyamanCreated Sep 16, 2026Updated Sep 16, 2026
Labelstriage

Capacitor Version

   Capacitor Doctor  

Latest Dependencies:

  @capacitor/cli: 8.5.2
  @capacitor/core: 8.5.2
  @capacitor/android: 8.5.2
  @capacitor/ios: 8.5.2

Installed Dependencies:

  @capacitor/android: not installed
  @capacitor/core: 8.5.2
  @capacitor/cli: 8.5.2
  @capacitor/ios: 8.5.2

[success] iOS looking great! 

Other API Details

Xcode 27.2 beta (27B5019j), also reproduced building with Xcode 27.0
macOS 27.0
Node 24.15.0
Swift Package Manager (the default for new Capacitor 8 apps)

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

Xcode 27.2 beta adds a JSON-based project format. Its release notes say it's "more readable, merge-friendly, and easier for coding agents to edit", and projects in this format also open in Xcode 27.0. Converting a project replaces App.xcodeproj/project.pbxproj with App.xcodeproj/project.xcproj.

The CLI still reads project.pbxproj directly, so after converting:

  • npx cap sync ios logs Unable to write to …/CapApp-SPM/Package.swift with ENOENT: no such file or directory, open '…/App.xcodeproj/project.pbxproj', but exits with code 0.
  • CapApp-SPM/Package.swift is never rewritten, so plugins you add or remove never reach iOS.
  • After removing a plugin, Xcode then fails with Could not resolve package dependencies, because Package.swift still points at the removed package.
  • npx cap doctor still reports "iOS looking great".

Xcode 27.0 and 27.2 both build the converted project fine, so only the CLI is affected.

Expected Behavior

The CLI works with both project.pbxproj and project.xcproj. At minimum, it should fail with a non-zero exit code and a clear message instead of reporting success.

Project Reproduction

With a blank app, no plugins of mine involved:

bash
mkdir blank-cap && cd blank-cap && mkdir www && echo '<h1>hi</h1>' > www/index.html
npm init -y
npm install @capacitor/[email protected] @capacitor/[email protected] @capacitor/[email protected]
npm install -D @capacitor/[email protected]
echo '{ "appId": "com.example.blank", "appName": "blank", "webDir": "www" }' > capacitor.config.json
npx cap add ios
npx cap sync ios            # works

# Convert to the new format (Xcode 27.2 beta); the same option is in Xcode's File inspector
xcodebuild -convert-project "Xcode Project" -project ios/App/App.xcodeproj

npm uninstall @capacitor/app
npx cap sync ios            # logs the ENOENT error above, exits 0
grep CapacitorApp ios/App/CapApp-SPM/Package.swift   # the removed plugin is still listed
xcodebuild -project ios/App/App.xcodeproj -scheme App -destination 'generic/platform=iOS Simulator' build
                            # error: Could not resolve package dependencies

Additional Information

Places on main that read project.pbxproj:

File What it does Effect with project.xcproj
cli/src/ios/common.ts, getMajoriOSVersion() Reads IPHONEOS_DEPLOYMENT_TARGET Throws. Called from util/spm.ts (this bug) and ios/update.ts
cli/src/ios/common.ts, editProjectSettingsIOS() Sets PRODUCT_BUNDLE_IDENTIFIER on cap add ios Would throw (new projects are still .pbxproj for now)
cli/src/tasks/migrate.ts Bumps the deployment target Throws
cli/src/tasks/migrate-uiscene.ts + cli/src/util/xcode.ts Adds SceneDelegate.swift through the xcode package, which only parses .pbxproj Throws
cli/src/config.ts, determineIOSWebDirAbs() Checks where public lives Error is caught, no effect

The new file keeps the same build settings as JSON keys, for example:

json5
"IPHONEOS_DEPLOYMENT_TARGET": "15.0",
"PRODUCT_BUNDLE_IDENTIFIER": "com.example.blank",

It allows trailing commas, so JSON.parse can't read it as-is. For the deployment target and bundle ID, falling back to project.xcproj when project.pbxproj is missing, with regexes that accept both KEY = value; and "KEY": "value", would cover cap sync, cap add and cap migrate. The UIScene migration needs its own change, since the xcode package can't edit the new file.