Getting 'no such module' error when importing a Swift Package Manager dependency

I'm running Xcode 11 Beta 4. I'm using CocoaPods, and wanted to use one of my dependencies with Swift Package Manager as a static library instead of as a framework. On a fresh project created with Xcode 11, the dependency can be imported successfully, but on my existing CocoaPods workspace, it does not.

I think it's likely related, but I'm also getting this link warning in Xcode:

directory not found for option '-L/Users/username/Library/Developer/Xcode/DerivedData/App-axanznliwntexmdfdskitsxlfypz/Build/Products/Release-iphoneos

在发出警告后,我去查看该目录是否存在,它确实存在。 I could not find any meaningful difference between the newly-created project and my old one, other than the existence of CocoaPods.

Would appreciate any pointers.

39318 次浏览

It turned out that Swift Package Manager implicitly depends on the project's Configuration names. I had them at live/qa instead of Release/Debug, and changing them back resolved the issue. Very odd, but I hope it saves you some trouble dear reader.

After a whole week fighting this issue, I developed a workaround using schemes and pre-actions.

I have a configuration called "Beta", so Xcode can't compile SPM dependencies. I realised Xcode compile SPM dependencies as Swift modules and add the files in Build/Products/Release-iphoneos folder in DeriverData.

So I created a scheme in Xcode and added this run script on build pre-actions:

enter image description here

cp -f -R "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/"

This script run before the build process, coping files and modules generated by Xcode on default Release-iphoneos folder to configuration folder, Beta-iphoneos, in my case.

After coping the content from Release-iphoneos to your $configuration$-iphoneos folder Xcode should correctly compile, build and run your project.

Based on @AlexandreMorgado answer it seems like it is better to run this script in Build phases before Compile Sources. Then it works when archiving.

enter image description here

if [ -d "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" ] && [ "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" != "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/" ]
then
cp -f -R "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/"
fi

In order to keep incremental builds working I had to specify the output files of "Fix SPM" build phase like so: enter image description here

Clearing the derived data solved the issue in my case. I have Microsoft Azure Devops CI Pipeline, to clear the derived data I have to edit the Xcode build task and in the "Actions" field add this command: clean.

After adding a library (FASwiftUI in my case) through Swift Package Manager I had to add it to

Project Settings -> My Target -> General -> Frameworks, Libraries, and Embedded Content

to be visible in the import statement.

I did not add any scripts for it to work.

enter image description here

I just ran into a similar problem and discovered that my schemes referenced old configurations, configurations that no longer existed. Once I updated them to the correct configurations the build succeeded.

(I'm leaving this comment more than a year after the original post. It's possible that what I ran into is completely different from what was originally reported. Still, it took me quite a while to track the problem down, so I wanted to leave a note that might save others time.)

What worked for me: I removed my import WebMIDIKit line and added it again.

I just ran into a similar problem when running xcodebuild from the command line. I was passing CONFIGURATION_BUILD_DIR=build but found that it needs to be an absolute path: CONFIGURATION_BUILD_DIR=$(pwd)/build solved the problem.

Solution

let package = Package(
name: "PackageName",
dependencies: [
// YOU MUST ADD THE DEPENDENCY BOTH HERE [1] AND BELOW [2]
.package(url: "https://github.com/mattmaddux/FASwiftUI", from: "1.0.4")
],
targets: [
.target(
name: "PackageName",
/*[2]*/ dependencies: ["FASwiftUI"], // [2] <<<--------- Added here as well
]
)

Explanation

I'm developing a Swift package that must provide FontAwesome Icons to whoever imports it.

I was getting "No such module 'FASwiftUI'" in my SwiftUI preview canvas.

I solved it by adding "FASwiftUI" to BOTH the dependencies array of the package AS WELL AS to the dependencies array in the target itself.

Full Package.swift File

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.


import PackageDescription


let package = Package(
name: "PackageName",
platforms: [
.macOS(.v11),
.iOS(.v14)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "PackageName",
targets: ["PackageName"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/nalexn/ViewInspector", from: "0.8.1"),
.package(url: "https://github.com/mattmaddux/FASwiftUI", from: "1.0.4")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "PackageName",
dependencies: ["FASwiftUI"], // <<<--------- Added this here
resources: [
.process("Assets")
]
),
.testTarget(
name: "PackageNameTests",
dependencies: ["PackageName", "ViewInspector"])
]
)

Based on @sliwinski.lukas's answer, in my case the ${CONFIGURATION} was outputting "Release", so it was just copying the Release folder itself which was no good. I simply hardcoded my configuration name, and flipped Release and MyConfiguration, and it worked. I put the following code right before "Compile Sources" in the "Build Phases" tab:

cp -f -R "${SYMROOT}/MyConfiguration${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" || true

Also importantly, I had to add this in the project that used the SPM and not in the main app.

Might I shed a bit more light on your plight...

I'm working on a fairly large iOS app (6680 files) whose result is composed of many frameworks and a mixed bag of podfiles, swift packages, legacy ObjC code (that still outnumbers newer Swift stuff).

Whenever we deal with swift packages, we need to wrap them in frameworks because it simplifies podfile & dependency resolutions when we have our remote (Jenkins) build system eat everything up to spew binaries for internal QA & ultimately, Enterprise & AppStore publishing.

Earlier today, I was dealing with one such swift package wrapped in a framework and all the issues listed above hit me square in the face.

After stashing, pushing usable code and then reapplying my stashed framework wrapper to the swift package, I used a different route than opening our project's workspace where a bunch of projects and targets are collected.

Opening the lone framework wrapper seems to have kicked XCode (13.3.1) into submission and at that point, the target settings "Frameworks, Libraries and Embeddable" section was actually able to display the swift package's "Foo" binary. Added it, and then everything was playing nice.

So, if you're still having problems, try simplifying the problem by opening smaller morsles if you can. Or start making these wrapper frameworks (if it's at all possible) so that you can actually manage smaller bites before integrating them on XC's platter.