Headers not found for an XCFramework packaging a static lib for iOS (critical)

With Xcode and frameworks it has been possible to wrap a static library, and have a framework statically link with that library. A good example would be an Objective-C wrapper for a C++ library.

I have been trying to accomplish such a thing with a Swift Package, and it seemed easy but I believe there might be a bug in SPM. Or I am doing something wrong but can't figure it out.
I pushed an example here GitHub - quentinfasquel/MyDependencySample

  • Packaging a static library along with its headers as an XCFramework is easy (using binaryTarget)

1- Using that library in a Swift Package and have it build is easy

2- Using that Swift Package in an app will not work as this Swift Package won't be able to compile.Somehow the XCFramework's headers don't seem to be found.

3- Manually dragging the .xcframework in your final target solve the issue (sometimes) but I find it inconvenient and not ideal to have a reference to "BUILD_PRODUCT/../../Package.."

4- Adding a product to the Swift Package for the binaryTarget and add that product to the final app target fixes the problem but does something not-OK : export libMyCppLibrary.a into the app bundle's Frameworks directory... that should never happen.

5- Also, back to the Swift Package outside an app, trying to archive it will not work and trigger the same "header not found" issue.

In need of help, or an official saying that this is a bug.

2 Likes

It does this for ALL dynamic Swift Package libraries. Really damn annoying. Anyone know a workaround?

There's a workaround with Xcode, a bit disappointing but it does the trick.. a script phase that removes the files that shouldn't be there.

Hey @quentinfasquel - I think I'm in a very similar boat. Were you ever able to get a static lib working as part of a Swift Package?

I've written a small lib in Golang and used go build --buildmode=c-archive which creates my header and archive file. I then packaged this into an XCFramework bundle using xcodebuild -create-xcframework -library {.a} -headers include -output {outfile}

I dropped this bundle in my Swift library project, along with a module.modulemap file in the same dir, where I have:

module {module_name} {
header "{.xcframework/path_to_header}"
export *
}

Where .xcframework is the actual bundle, followed by the path directly to the header file (in my case, I'm only building for macOS at the moment, so I'm pointing directly to the macos-x86_64 archtype).

Finally, in my Package.swift, I have a .binaryTarget entry whose path points to the above .xcframework file. I also had to include this section in the actual lib target:

cSettings: [
.headerSearchPath({relative_path_to_parent_folder_containing_modulemap}),
]

In all my searching, it seems like using headerSearchPath isn't very common - I don't know why I need it, but I digress. The fact is...this all compiles fine with swift build, and in fact my main class is able to import the aforementioned module AND I can access the method(s) exposed as defined by the header file. In other words...seems great, no?

Problem is - despite it compiling, when I pull it in as a Swift Package Dependency in my actual app (using the remote option - I have this all up on git), and I try to import it - the actual import name is found, but it gives me an error saying: "Missing required module '{module}'" - where module matches the name I defined in the modulemap file.

For what it's worth, I was actually able to get the app project working when the header/lib source was locally in the project itself, using a similar module.modulemap file and some small tweaks in the build settings (i.e. updating the Swift Compiler Import Paths and Library Search Paths to point to $(SRCROOT) which is required for it to pick up the modulemap). Unfortunately it all broke when I used Swift Package Manager to pull in some other remote dependencies. It seems like once I added those, it stopped honoring the import/search paths. I don't have a strict requirement for this static lib to be a remote package or xcframework bundle - but I couldn't figure out a workaround after introducing the remote dependencies.

Exact same boat... and the boat is still rocking.

exactly the same problem, few hours on this :sleepy:

Is there any solution for this? I am running into exactly the same issue.

For me it always works the second time I build without cleaning before. Which is okay for local builds but not acceptable on the build server.

I filed a bug report on JIRA about this a while back, and as of Xcode 13 beta 5 it's mentioned as a known issue in the release notes, so in the very least it's on Apple's radar (pun intended). The best bet right now is probably to upvote the issue, and perhaps comment with more ways to reproduce the seemingly non-deterministic setup that it requires.

1 Like

At least for me it is not non deterministic. It fails every time, if there is nothing already there from a previous build.

Do you have any workaround? I tried some of the suggested approaches from the OP but they seem to fail due to a slightly different setup in my case (the problematic package is only a dependency of another package which then is a dependency of my project).

Edit: Found a workaround which works for me. I created an additional standalone Swift package with just a Package.swift file nothing else, which contains the binary target and a library product for that target. I can depend on that package in the other package and everything works just fine.
I was wrong, I still have the same error with that setup it seems.

Manually adding .headerSearchPath to dependent C targets (similar to what @ryanleesmith described) fixes the issue for me, but it doesn't appear to work when the dependent target is Swift-based – besides being buggy/limited in a lot of other ways.

2 Likes