You may have heard about xcframework
s and how they are a way to distribute libraries on Apple platforms. But documentation on the format is hard to come by. With this blog post I hope to summarize everything you need to know about xcframework
s and put it all in one place for your reference.
This section provides a rationale for the existence of xcframework
s. Feel free to skip it if you’re not interested.
Before xcframework
s were defined, there was no Apple-supported way to distribute libraries for more than one platform at a time. This wasn’t really a problem for a while, since the only Apple platform a developer could build a framework for was OS X. Even when the Mac switched from PowerPC to Intel, the single-framework model survived because it was possible to create Universal “fat” binaries that contained more than one architecture slice. Since a framework would still target a single platform, including a Universal binary worked quite well.
When Xcode began supporting frameworks for the iPhone SDK, the picture became more complicated, since developers had to deal with two new platforms: iPhoneOS and iPhone Simulator. Developers could ship two frameworks—one for each platform—but it was not easy to consume two distinct frameworks in a project that should be easily built for both iPhone hardware and the simulator. While build-settings tricks could be employed to switch FRAMEWORK_SEARCH_PATHS
depending on the build platform, the lack of automatic support in Xcode for doing so made it a difficult chore.
One workaround that some developers adopted was to use the lipo
tool to splice an arm64
slice from the iOS build of the framework, together with an x86_64
slice from the Simulator build, and distribute a single “Frankenframework” that could be used to build both a device binary and a Simulator binary, without build-settings shenanigans. This hack conflates architecture with platform but it worked…
…until the M1 Mac came along. Since the M1 Mac natively used the arm64
architecture, both iPhone hardware and Simulator builds consumed the arm64
architecture; developers could no longer use architecture as a proxy for platform. Add to that the proliferation of platforms—tvOS and watchOS and their simulators—and it was clear that a better solution was needed.
Apple defined the xcframework
format as a way to cut through this chaos. This officially-supported specification allowed developers to distribute the same library built for a variety of platforms and architectures in a single package. Projects that consumed an xcframework
needed only specify a single link dependency, and Xcode would switch between platforms and architectures automatically. The xcframework
specification is future-proofed enough to accommodate new platforms or architectures Apple might support in the future.
An xcframework
is a library distribution bundle
To be precise, an xcframework
is a universal, binary, library distribution format. Let’s break that description down in reverse order.
An xcframework
is a library distribution format. Each xcframework
holds exactly one library. A library is a precompiled collection of code that can be consumed by another project to create an executable (or app).
An xcframework
is a binary distribution format. That means it does not include source code in its distribution. Only built binaries and interface specifications (headers and/or Swift interface files) are included.
An xcframework
is a universal distribution format. That means it holds libraries and interfaces for different platforms as well as processor architectures in the same structure. A single xcframework
can, for example, offer the same library for consumption for iOS, watchOS, and Mac projects using either Intel or ARM architectures.
Finally, an xcframework
is a bundle because it’s a directory with a well-defined content structure and file extension, and has an Info.plist
file in its root. Examining its Info.plist
file shows that it has a CFBundlePackageType
of XFWK
.
Read more…