Custom Plugin unable to use 3rd party package with in Capacitor 8 (with SWP)

We have a custom Capacitor plugin being used by our Capacitor mobile app on the iOS platform. The custom plugin requires a 3rd-party package that is distributed to us in the form of a “.xcframework” package. We have been building this plugin successfully in Capacitor 7, but now that we’ve upgraded to Capacitor 8, it cannot resolve the 3rd-party package.

I see that all plugins generated with “create-plugin” do have a file called Package.swift that is automatically generated. XCode documentation suggests that you can add a “.binaryTarget” to the Package.swift file to link against a 3rd-party library. Of course I’ve added such a line to the Package.swift file, but it does not work. The line of Swift code “import ThirdPartyModuleName” produces an error “No such module ‘ThirdPartyModuleName’”, and the project will not build. Here is the Package.swift file (note the .binaryTarget entry referencing the 3rd-party library package):

import PackageDescription

let package = Package(
    name: "ValmarcCortex",
    platforms: [.iOS(.v15)],
    products: [
        .library(
            name: "ValmarcCortex",
            targets: ["CortexPlugin"])
    ],
    dependencies: [
        .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
    ],
    targets: [
        .target(
            name: "CortexPlugin",
            dependencies: [
                .product(name: "Capacitor", package: "capacitor-swift-pm"),
                .product(name: "Cordova", package: "capacitor-swift-pm")
            ],
            path: "ios/Sources/CortexPlugin"),
        .binaryTarget(
            name: "CortexDecoder",
            path: "ios/CortexDecoder.xcframework"),
        .testTarget(
            name: "CortexPluginTests",
            dependencies: ["CortexPlugin"],
            path: "ios/Tests/CortexPluginTests")
    ]
)

Since the above Package.swift suggestion doesn’t work, my question is as follows: In Capacitor 8 with SWP, what is the recommended way to link against a 3rd-party “.xcframework” library with a custom capacitor plugin? The Capacitor documentation doesn’t really cover this case.

Thank you for any help you can offer.

You need to add CortexDecoder to the dependencies list of your CortexPlugin target.

.target(
    name: "CortexPlugin",
    dependencies: [
         .product(name: "Capacitor", package: "capacitor-swift-pm"),
         .product(name: "Cordova", package: "capacitor-swift-pm"),
         "CortexDecoder" // <--- HERE
    ],
    path: "ios/Sources/CortexPlugin"),

Targets are isolated by default, so you must explicitly list CortexDecoder as a dependency for CortexPlugin to “see” it. This tells the compiler to include the framework’s headers and link the binary so your import statement works.

1 Like

@Hills90210 That worked! Thank you so much!

I did some more research and I also found another entry in the Package.swift file that seems to solve the problem. If I make the following change to the Package.swift file, then it also seems to work:

let package = Package(
    name: "ValmarcCortex",
    platforms: [.iOS(.v15)],
    products: [
        .library(
            name: "ValmarcCortex",
            targets: ["CortexPlugin","CortexDecoder"])  // <--- Added CortexDecoder HERE
    ],
    dependencies: [
        .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
    ],
    targets: [
        .target(
            name: "CortexPlugin",
            dependencies: [
                .product(name: "Capacitor", package: "capacitor-swift-pm"),
                .product(name: "Cordova", package: "capacitor-swift-pm")
            ],
            path: "ios/Sources/CortexPlugin"),
        .binaryTarget(
            name: "CortexDecoder",
            path: "ios/CortexDecoder.xcframework"),
        .testTarget(
            name: "CortexPluginTests",
            dependencies: ["CortexPlugin"],
            path: "ios/Tests/CortexPluginTests")
    ]
)

Note that I added the “CortexDecoder” library as target in products.library.targets. With the above Package.swift file, I am able to build our iOS project.

So it looks like there are 2 ways to solve the problem:

  1. Add CortexDecoder as a dependency in the targets.target.dependencies array.
  2. Add CortexDecoder as a target to the products.library.targets array.

What is the difference between these two approaches, and why would I use one over the other? I’m not a Swift package manager expert, so I appreciate any advice you can offer.

1 Like

The first approach explicitly sets the target in the library, whereas the second uses the project’s library settings. You might prefer the first for better encapsulation and modularity or the second for ease of management and consistency across your project.