"Missing required modules" when importing a SwiftPM framework

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?

The package manager does not create frameworks (.framework bundles). What file are you dragging? (.dylib, .swiftmodule, ...?)

What do you mean by “project”. Is that another package repository? Is that an Xcode project?

It can make Xcode create one by using generate-xcodeproj. Is that part of what you are doing?

The answer is probably yes. I just need more information to actually help you straighten it out.

Yeah, I'm using generate-xcodeproj to generate a .framework and dragging that in. Here it is if you're curious about the setup

Each .target in your package manifest becomes a separate .framework bundle when you do generate-xcodeproj, with each one linking dynamically against the others.

When using them in another project, you have to embed every .framework relevant to the dependency tree.

In your case that means DiscourseKit.framework, Networking.framework and Extensions.framework must all be embedded.

Oh, yikes. That's not obvious because the others don't show up in Products. Thank you!

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.

Gotcha.

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

Edit:

  • 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 :sweat_smile: 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 :confused:

I have not actually tried to embed one in an application bundle and I
do not know what App Store implications there may be.

Which is isn’t really relevant to the main thrust of this thread, I am able to clarify this for you, to wit, it depends on your target store:

  • Embedded shared libraries (.dylib) are absolutely supported in Mac App Store apps.

  • iOS does not support third-party embedded shared libraries [1]. You would have wrap such a library in a framework (.framework).

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

[1] It does support embedded shared libraries in third-party apps, but that’s solely to allow folks to embed the Swift standard library.

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",
    platforms: [.iOS(.v11)],
    products: [
        .application(name: "My App", targets: ["My-App"])
    ],
    targets: [
        .target(name: "My App", path: "Sources/My App")
    ],
    dependencies: [
        .package(url: "https://github.com/NSExceptional/DiscourseKit.git", from: "0.1.0"),
    ],
    swiftLanguageVersions: [.v5]
)

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.)