Skip to content

[Bug]: SwiftPM identity collision between plugin and transitive dependency #8451

@robingenz

Description

@robingenz

Capacitor Version

💊   Capacitor Doctor  💊

Latest Dependencies:

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

Installed Dependencies:

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

[success] iOS looking great! 👌
[success] Android looking great! 👌

Other API Details

npm --version output: 10.9.4
node --version output: v22.22.0

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

When two Capacitor plugins are installed and the last path components of their npm package directories collide, the CLI-generated CapApp-SPM/Package.swift declares two local-path packages whose SwiftPM identity (the lowercased path basename) is the same. SwiftPM rejects this.

The collision can take two forms with different severities:

Example A — hard error (two local plugins). @capacitor/app + @capacitor-firebase/app. Both resolve to npm directories ending in app, so both get SwiftPM identity app:

Conflicting identity for app: dependency '.../node_modules/@capacitor/app'
and dependency '.../node_modules/@capacitor-firebase/app' both point to
the same package identity 'app'.

The build fails. This is the long-standing local-vs-local hard-error path in SwiftPM and is unrelated to any specific Xcode version.

Example B — warning, future hard error (local plugin vs. transitive remote dependency). @capacitor-firebase/app-check + @capacitor-firebase/authentication. The Authentication plugin pulls in firebase-ios-sdk, which transitively depends on github.com/google/app-check. Both that remote package and the local App Check plugin directory get identity app-check:

Conflicting identity for app-check: dependency 'github.com/google/app-check'
and dependency '.../node_modules/@capacitor-firebase/app-check' both point
to the same package identity 'app-check'. This will be escalated to an
error in future versions of SwiftPM.

Currently a warning, but SwiftPM itself states it will become an error.

Root cause. The CLI emits .package(name: "<plugin.ios.name>", path: "<plugin.rootPath>") in cli/src/util/spm.ts. SwiftPM ignores the name: argument for local-path packages (post-SE-0292) and derives identity from the path's lowercased basename. The basename equals the npm package directory name and cannot be changed without renaming the npm package — which is a breaking change for plugin consumers.

Expected Behavior

Plugin authors should be able to point the CLI at a uniquely-named subdirectory containing Package.swift, so SwiftPM derives a unique identity from that subdirectory's basename instead of the npm package directory's basename.

Concretely: a plugin's package.json declares e.g.

{
  "capacitor": {
    "ios": {
      "src": "ios",
      "spm": { "path": "ios/spm/CapacitorFirebaseAppCheck" }
    }
  }
}

The CLI would then discover Package.swift at <rootPath>/ios/spm/CapacitorFirebaseAppCheck/Package.swift and emit:

.package(name: "CapacitorFirebaseAppCheck", path: "<rel>/.../node_modules/@capacitor-firebase/app-check/ios/spm/CapacitorFirebaseAppCheck")

SwiftPM identity becomes capacitorfirebaseappcheck → no collision in either Example A or Example B. Plugins that do not set capacitor.ios.spm.path keep their current behavior, so the change is fully backward compatible.

Project Reproduction

https://github.com/capawesome-team/capacitor-firebase-plugin-demo/tree/chore/ios-spm-migration

Additional Information

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions