How to build Swift Package as XCFramework

Well I see it a bit like the asset catalog support. That's an Xcode feature (if I'm not mistaken), and is a detail of the private SPM fork that Xcode uses (handwavy and probably technically inaccurate).

but we digress from the original topic… sorry

swift build supports consuming XCFrameworks imported via .binaryTarget(). It's not just an Xcode feature.

2 Likes

Has anyone filed an ERs are about this? If so, I’d appreciate you posting any FB numbers (for xcodebuild) or issue links (for SwiftPM).

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

Is there a solution to this yet?

Super annoying when trying to make a prebuild SPM plugin because they require a binary target.

1 Like

I'm struggling to make it work with an SPM that uses @_exported import.

The Library i try to wrap into the xcframework uses a shared target. Upon importing the created xcframework it throws the issue No such module (for the @_exported import).

Any idea how to resolve it? Are only the module headers missing or is the code actually missing?

Building an XCFramework is not a platform support, it's Xcode support. I'm not convinced that building an XCFramework is needed at all if you want to use a shared library. Why not just build a dynamic library? That should work on any platform, including Apple platforms.

1 Like

A distinction without a difference given that Xcode bundles all of the platform support tools. Supporting Xcode is supporting the platforms. xcframeworks are the only way to produce universal frameworks for Apple platforms. This is not a secret process, and Xcode uses libSPM to enable such builds internally. Making SPM itself aware of the format simply moves the support logic down a level and could allow better support for cross compilation. Hell, Apple could limit such support to their version of SPM, they build all of the tools for themselves anyway.

3 Likes

I didn't know that. BTW, what is the difference between "universal frameworks" and "shared libraries"?

If you need just a shared library, here's my example on how one can do that (a proof of concept, to demonstrate that what works on other platforms, certainly works on a Mac):

  1. Create a dynamic library:
mkdir test
cd test
swift package init

// Modify the Package.swift *products* section so that the package would produce a *dynamic* library:
.library(
    name: "test",
    type: .dynamic, // specify this to create a dynamic library
    targets: ["test"]),

// Add some public code into the test.swift file, e.g.
public func meaningOfLife() -> Int {
    return 42
}

swift build -c release
  1. Create a host app that uses shared code from the library:
cd ..
mkdir app
cd app
swift package init --type executable

// Add some code to main.swift that uses shared code from the library, e.g.
import test
print("Meaning of life is \(meaningOfLife())")

// Build the app and link it against the library:
swift build -c release -Xswiftc -I -Xswiftc "./../test/.build/release" -Xswiftc -L -Xswiftc "./../test/.build/release" -Xswiftc -l -Xswiftc test
  1. Copy the library to the same folder as your app executable:
cp ./../test/.build/release/libtest.dylib .build/release/
  1. Run the created app:
.build/release/app
  1. See the output
Meaning of life is 42

I say universal because they can be truly universal across all of Apple's platforms. A single bundle can contain macOS, iOS, iOS simulator, and everything else.

But of course we've had dylib support forever. But it's not what we use on Apple platforms. Before .xcframeworks there were .frameworks (with a history going all the way back to NeXTStep) which can contain what are essentially dylibs (even fat dylibs (PPC/Intel, Intel/AS)) with all sorts of other resources. They can even contain multiple versions, though Apple has never used this capability. xcframeworks go one step beyond frameworks and allow ABI incompatible (probably not the right term) platforms to bundle their frameworks together into one super bundle. It's mostly a bundling mechanism but it requires particular structure and metadata. But it's truly universal: every OS, every simulator, every architecture, all in one bundle.

1 Like

Ah, now I see. I think these are cool for using in Xcode. The original question by @Srdan_Rasic was about

I thought that dylib files would be a solution for that, especially when using Swift Package Manager rather than Xcode.

I thought that dylib files would be a solution for that

It depends on what you mean by “universal” |-:

On Apple platforms a universal Mach-O can hold multiple architectures for the same platform. However, if you want to support multiple platforms you need an XCFramework. And, critically, iOS and the iOS Simulator are different platforms, so this requirements comes up all the time.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

@Srdan_Rasic Although it works for a simple SPM without any SPM dependencies, it fails when at least one SPM dependency exists. Is there something I am missing?

2 Likes

There was a request here and in the linked Apple Developer Forum post to create Feedback asking for this functionality. If it helps, I created FB13812783 to request this capability built-in to Xcode.

4 Likes

Forked variant has had updates which seems to work for me.

Although in some cases I needed to use

@_implementationOnly import Kingfisher

or new:

internal import Kingfisher

in order for projecting linking xcframework to work.

1 Like

Hello, since this thread is years old, I wanted to ask about the latest status.

If I want to compile an xcframework from an SPM package, is it still the case that the only way to do that is by using the third-party tool from segment-integrations?

Just to ensure I'm not committing an XY problem, here's what I'm actually after in the form of a specific question. Given that:

  • There are two approaches to pulling in dependencies: as source code (Cocoapods, SPM) or as compiled frameworks (Carthage)
  • In large-scale projects, source-based dependencies cause performance issues with Xcode
  • Carthage requires a .xcodeproj file to be present in order to build compiled frameworks
  • Some SPM packages do not include .xcodeproj files at all (e.g. SwiftNIO)
  • the generate-xcodeproj subcommand has been removed from SPM

if I wanted to compile a dependency like SwiftNIO as xcframeworks and pull them into my project, what is the current practice for doing so?

As there is no official support for building XCFramework from a Swift Package yet, the swift-create-xcframework seems to be the best solution I found so far among many.

The idea behind that tool is to build from a Swift package Package.swift, so I'm not sure the relevance of needing a .xcodeproj for SwiftNIO as you mention?

My understanding is the Xcode 16 updates to Explicitly Built Modules should optimise the source-based dependencies:

1 Like