Compiling Package.swift in a raw Xcode project
Let me first start with why I even wanted to compile
Package.swift in a raw Xcode project, instead of Apple's default package integration.
Tuist has long had a custom integration of Swift Package Manager dependencies where instead of using the default Xcode integration, which has a myriad of issues, we convert the SPM projects to regular Xcode projects.
For defining the list of dependencies, we've used a
Dependencies.swift manifest but using a custom manifest file broke tools like dependabot. So, for Tuist 4 we're fully moving to
For users, to be able to edit
Package.swift as any other Tuist manifest, we wanted to include it in the project generated by the
tuist edit command.
The how #
Let's start with the first naive approach – simply including the following
Package.swift in an Xcode project:
// swift-tools-version: 5.9
let package = Package(
.package(url: "https://github.com/Alamofire/Alamofire", from: "5.8.0"),
If you do that, you will get the following error:
No such module 'PackageDescription'. The
PackageDescription framework is not included in Xcode projects by default but is inserted by Xcode as part of the default SPM integration.
But where can we find
PackageDescription? We can use the
swift package describe --verbose command which evaluates the
Package.swift – and to do that, it needs to include the
A part of that output is, indeed,
-L /Applications/Xcode-15.1.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/ManifestAPI -lPackageDescription where
-L is defined as "Add directory to library link search path" and
-l as "Specifies a library which should be linked against". So, the Swift compiler knows it should try to search for
PackageDescription and it also adds the
ManifestAPI path to its search paths. The
ManifestAPI then contains the
PackageDescription itself (more specifically,
We can now leverage this in our raw Xcode project by adding the
ManifestAPI path to the
FRAMEWORK_SEARCH_PATHS and voila, the project now builds ✨
Swift tools version #
But that's not all. We have successfully added the
PackageDescription framework, however, we currently don't respect the
swift-tools-version defined at the top of the
Package.swift file. Our raw Xcode project treats
Package.swift as any other Swift file, so how could it?
Going back to the output of
swift package describe --verbose, we will also find the following Swift flag:
-package-description-version 5.9.0. For our raw Xcode project, we can simply include this Swift flag in
OTHER_SWIFT_FLAGS and that's it! If we need to get the version number dynamically (as we do in tuist), we can use the
swift package tools-version command.
Afterwards, building the
Package.swift will succeed and use the right Swift tools version. If you are interested in looking at the actual PR we did for
tuist edit, you can find it here.