"No such module" when using SwiftPM binaryTarget/XCFramework on dependency

My setup is as follows:

  • iOS App (Executable) implements Library A via SPM
    • Library A implements StaticLibrary B through .binaryTarget option
    • Library A imports StaticLibraryB in several swift files and compiles fine on it's own

The problem starts when I try to archive the iOS App as I always end up getting the error no such module 'StaticLibraryB' on the first of my swift files that import my StaticLibraryB in Library A.

The binaryTarget itself is a zipped xcframework and works fine when manually added to Xcode (not via Package.swift), so I don't think that is killing the compilation.

Looking at other posts, I've already tried putting the binaryTarget into it's own product via a new Package.swift file and then dropping the binaryTarget dependency to no avail. Importing StaticLibraryB with @_implementationOnly didn't fix it for me either.

Looking at my build log though, it seems that when Xcode invokes swift and SPM to compile LibraryA, it's not processing the xcframework correctly. I've seen this race condition happening before on it's own, where on a cached build SPM correctly uses the builtin-process-xcframework option to process XCFrameworks but not on clean builds. Unfortunately, I can't replicate this race condition here no matter how often I try to archive.

I'd appreciate any insights on how we could solve this issue to get past this point for compilation.

4 Likes

My team is experiencing exactly the same problem with the same setup. Happens with Xcode 13 too. It's also listed under "Known issues" in the release notes, so there is hope: Apple Developer Documentation

1 Like

Is there any workaround for this issue?

I had a similar issue in one of our packages when using binary frameworks, where we had to create C target wrapper with .c and .h files, so now this C target wrapper depends on the binary target, here is an example how it needs to be structured in the package

Here is how the package manifest should look

import PackageDescription

let package = Package(
    name: "TestPackage",
    platforms: [
        .iOS(.v15),
        .macOS(.v12)
    ],
    products: [
        .library(
            name: "TestPackage",
            targets: ["TestPackage"])
    ],
    dependencies: [],
    targets: [
        .target(
            name: "TestPackage",
            dependencies: [
                "TestWrapper"
            ]
        ),
        .target(
            name: "TestWrapper",
            dependencies: ["Test"],
            path: "TestWrapper/TestWrapper"
        ),
        .binaryTarget(
            name: "Test",
            path: "Frameworks/Test.xcframework"
        )
    ],
    swiftLanguageVersions: [.v5]
)

Then add your binary framework header import to .h file in the C target wrapper

#ifndef Bridge_h
#define Bridge_h

#ifdef __OBJC__

// Add all required headers from the binary framework.
#include "Test.h"

#endif


#include <stdio.h>

#endif 

Then in your package just import the C target which will provide access to the binary package inside your package

import TestWrapper
4 Likes

works well and really good work around.

Hi @kvarma, thanks for the example!

I've tried to replicate that in a sample project that the binary has a SPM dependency, to see if it fixes my issue (which is different)

In the .h in the C target wrapper I just imported the whole package #import <MyBinaryFramework/MyBinaryFramework-Swift.h>.

Still, no luck. "Library not loaded: @rpath/Valet.framework/Valet" which is my binary framework's dependency.

If you do have a second to have a look at the sample project, it's here GitHub - CassiusPacheco/MyBinaryFrameworkUsageSample at c-wrapper (c-wrapper branch).

There's a Sample folder with the Sample project that points to that specific commit that implements this details.

Thanks again