iosdev

Strange issue adding SPM package as dependency on another package

Unstucking SPM resolver graph.

Swift Package Manager dependency resolution mostly work logically:

① First you specify the dependency in any number of ways, here’s the most usual one:

dependencies: [
	.package(
		url: "https://github.com/radianttap/Alley",
		from: "3.0.0"
	),

② Then you reference dependent library by its product name:

targets: [
	.target(
		name: "Atlas",
		dependencies: [
			"Alley"
		],

Alley is simplest possible library - it has one target, called like the package, no sub-dependencies. There’s no confusion how to reference it. Most packages are like this.


But things are not always that simple. Let’s use Auth0’s iOS SDK as example, which is at this URL:

.package(
	url: "https://github.com/auth0/Auth0.swift",
	from: "2.7.0"
),

If this is added as dependency to an iOS app, it works as any other package — you do import Auth0 where needed and all is fine.

But if we add this package as dependency to another package (here called Backend):

targets: [
	.target(
		name: "Backend",
		dependencies: [
			"Auth0"
		],

we get this confusing error:

product 'Auth0' required by 
package 'backend' target 'Backend' not found.
Did you mean 'Auth0'?

Auth0 package is correctly resolved and checked-out, along with its 2 sub-dependencies. There’s no other package nor target called Auth0 thus I don’t really get what’s confusing SPM dependency resolver.

For whatever reason, we need to explicitly point where the library product is:

targets: [
    .target(
		name: "Backend",
		dependencies: [
			.product(name: "Auth0", package: "Auth0.swift")
		],

Why this? Let’s look at package manifest:

let package = Package(
    name: "Auth0",
    products: [.library(name: "Auth0", targets: ["Auth0"])],

I admit I haven’t delved deep into how naming resolution in SPM works thus naively I can conclude that package.name is pretty much irrelevant and that resolver looks into ending part of the repo URL, which is Auth0.swift and uses that to reference the package.

I encountered the similar issue with a package used on another project. It’s called IPificationSDK but here’s how it is loaded:

.package(
	url: "https://github.com/bvantagelimited/IPificationSwiftDistribution.git",
	exact: "2.0.12"
),

and of course, this is how it’s referenced:

targets: [
	.target(
		name: "WrapperService",
		dependencies: [
			.product(name: "IPificationSDK", package: "ipificationswiftdistribution")
		],

I’ve seen a similar thing happen if you add binary-distributed library as package dependency. It’s actually the search for answer to that problem that lead me to this SwiftPM forum post which provided the solution.

It’s incredible to me that I have not encountered this .product(name:package:) nugget anywhere else — not in Apple’s videos or documentation guides nor any of the blog posts that are returned as top search results for these keywords. Every article out there provides examples with simple String values.

Apple definitely needs to hire more technical writers.