I have an SPM package, which declares several targets within itself that are used to make the final framework.
When I drag the framework into another project, I can use the types defined in the framework, but some import statements give Missing required modules: X, Y where X, Y are some of the targets in the framework.
Not sure what I'm doing wrong or not doing at all; aren't we supposed to be able to use SPM frameworks like this now?
At the moment, the distinction between targets and products is unique to the package manager and not inherited by Xcode. Products built directly by the package manager ($ swift build) do contain all their component modules along the lines of what you expected.
Is there no way to get SPM to output a .framework using swift build? Or a way to configure your Package.swift such that a generated Xcode project builds a single framework by statically linking the other targets?
Edit: I've done as much research and tinkering as I can, and I've come to the conclusion that SPM really isn't ready for this use case yet—that is, using it to build a standalone framework or static library that I can easily copy into another project (such as an iOS app), without doing something fragile like including the SPM project in a workspace. Would you say that's correct?
I've just run into so much friction that I'm ready to give up on it even if that isn't the case:
SPM generates products inside a hidden .build folder by default (why?) while also not providing a way to change the product output folder, AFAICT (so, not the entire build folder I mean)
SPM-generated Xcode projects ignore type: .static in the Package file
As you revealed to me, SPM-generated Xcode projects generate multiple frameworks which dynamically link against one another—one framework per-target—which is undesirable in every case I use can think of
Since the generated Xcode project seems to ignore important settings in the package file, it seems useless to me unless you're making a simple single-target framework or an executable
Since a lot of the problems above stem from SPM not handling targets nicely, it discourages the use of modular code, because these problems would go away if I only had one target
swift build seems to only build for x86_64 even though I have iOS set as a supported platform
You can make it create a dynamic library (with its own dependencies statically linked). Compare the two products of the SwiftPM package. Such a library would itself be a dynamically linked dependency just like a framework. The difference is that it is a single executable file, not a bundle with resources. Maybe that will suffice for your case, though I have not actually tried to embed one in an application bundle and I do not know what App Store implications there may be.
At the moment, that is the recommended vanilla method. I am not entirely sure what you mean by the words “copy into another project”. The package manager is designed to vend source code, not compiled binaries. Manually dragging products around where they cannot be rebuilt when needed is significantly more fragile than letting the package manager or Xcode manage the dependency tree. Right now Swift does not provide for module stability—it passed evolution review only a few days ago.
I usually get around this in scripts by dynamically querying the package manager for the location with swift build --show-bin-path.
I have long wondered about these myself. Maybe @Aciid knows the reasoning?
In general the inter‐op of the package manager with Xcode does still need work, though it is getting better all the time. In the meantime there are many third‐party tools out there attempting to fill in the gaps until official solutions arrive. @Jeehut has mentioned Accio in a similar thread. Maybe he could tell you if it solves your particular pain points.
Well, the problem with that is you need the resources that go with it if you want to distribute it. You can't just release the .dylib, you need the modulemap and the swiftdoc, so you have to bundle that into a framework by hand, which isn't great.
I am not entirely sure what you mean by the words “copy into another project”.
As in, taking a pre-compiled library and dragging it into an Xcode project But you're right, that's probably not how I should be doing it anyway.
Funnily enough this brings me to another pain point: the documentation says nothing about how to install packages into an existing project. Am I just supposed to clone the repo, generate the Xcode project and put it in a workspace? If that's the case, now we're back to the generate-xcodeproj related problems
Packages are not designed to be “installed”. The idea is you declare them as a dependency in your own package in order to have direct access to the source code. It works wonderfully on desktop platforms, but since there is no support for resources yet, application and framework bundles are not directly supported. A consequence is that the CocoaTouch platforms have no viable product types and have to work around it through generate-xcodeproj. (I posted a precise walkthrough over on this thread.)
Theoretically, someone who wants to use your library should simply create a package of their own that depends on it. Exactly how your package’s sources end up built into their product is then their own decision—including whether or not to use Xcode, Mint, Accio or whatever other package handling tools are out there. Note for example that by default, package libraries do not produce any executable‐like output file of any kind, but are built right into whatever depends on them. Eventually the intent is that optimization will be able to cross module and package boundaries, resulting in tailored optimizations according to how the library’s symbols are actually used.
There is no reason your package has to only be a Swift package. If you think your users would prefer it, you can simultaneously vend it for use through other dependency managers like Carthage or Cocoapods.
Yeah, that's what I meant when I said "installed". That's how pod install works for CocoaPods, for example.
A consequence is that the CocoaTouch platforms have no viable product types and have to work around it through generate-xcodeproj . (I posted a precise walkthrough over on this thread.)
Theoretically, someone who wants to use your library should simply create a package of their own that depends on it.
Gotcha. Can you elaborate on that? Does this mean there's no "officially sanctioned" way to use an SPM package in an iOS app for example? (Other than your walkthrough, which I have feared might be the only way)
I'm late to the table so I'm sure this has been discussed before, but ideally I'd like to be able to have a manifest file like this which generates an iOS app project:
let package = Package(
name: "My App",
.application(name: "My App", targets: ["My-App"])
.target(name: "My App", path: "Sources/My App")
.package(url: "https://github.com/NSExceptional/DiscourseKit.git", from: "0.1.0"),
Since that's not possible right now, I'd like to be able to do any of these in the meantime:
Tell SPM to generate an Xcode project which generates a single library, either as a "static library" or a "framework"
As a workaround to the above not being possible, being able to specify an .xcconfig via --xcconfig-overrides which would alter how the Xcode project is generated to my liking (I don't know if xcconfig can change how targets are linked together, or how products are built, etc)
Tell SPM to build a single library for all specified platforms—currently it will build a static library just fine, but only for x86_64 for whatever reason
Unless there's a relatively straightforward way to get SPM to do anything like the above, complex packages are not suitable for use in actual projects at all, imo
Me too. Unfortunately there is a lot that needs to happen to make that possible. The cross‐platform nature makes it more complicated for SwiftPM than it is for Xcode. Xcode does not have to deal with Linux, Android or Windows, none of which have a concept of an .app or .framework and need some other interoperable equivalent. It may take some time for this to become a reality.
There is no official way to build a Swift package for iOS at all. Cross‐compilation is not supported yet. All you can do is let the package manager find the sources for you, and then use them with some other tool, which is basically what the various workarounds and third‐party tools try to make easier.
They are suitable, but they may not be easy to apply to the particular kind of project you work with yet. I do not disagree that packages are not nearly as convenient as they could be for iOS applications.
My advice would be:
Wherever you feel packages do not do a good enough job on their own yet, fill in the gaps with the third‐party tools or use parallel dependency managers instead. I mentioned several of each above.
Design and organize your projects with the package manager in mind, even where you are not using it yet, so you will not have to change much when it has improved enough that you are ready to adopt it.
Vend any libraries as packages in addition to whatever other methods, because there are users out there who will prefer to use them as Swift packages over other formats. (I am one such person.)