XCFrameworks Internals: A container for iOS frameworks

Igor AsharenkovMarch 11, 2023

Share:

This year at WWDC, Apple introduced a new code distribution format – XCFrameworks. As SDK developers, we were excited about this news. Distributing your code as a library has issues related to the Apple ecosystem and tech stack.

We can't include Swift code in our library. We must consider our dependencies and iOS simulator slice when users upload their apps to the AppStore. We've looked through session 416 and decided to build AppSpectorSDK as an XCFramework. Before going through the steps of creating an XCFramework, let's recap the framework.

Static and dynamic are two main words you often hear while talking about libraries. These terms refer to how library code is loaded and linked to the primary process.

Static libraries copy to the app binary at compile time. A dynamic library is loaded at runtime during startup or triggered by the first reference to the symbol inside it. Then dynamic frameworks distribute in the form of frameworks on Apple platforms.

The framework is a bundle or a directory with a particular structure. In the case of a framework, it contains library code and a plist file with some meta info about the framework.

Generally, framework usage is the preferred way to ship your code for various reasons—for example, smaller binary sizes and more accessible updates. In most cases, code you use from Apple SDKs provides a framework loaded directly from the OS you are running on.

New framework format

A couple of weeks ago, we were introduced to a new framework format - XCFramework - another bundle type. It is similar to the framework but contains code for multiple architectures and platforms. You can ship slices for any architecture, including simulator, Apple OS, and even separate portions for UIKit and AppKit apps.

Apple came up with this to make porting the code to macOS easier. It eliminates lipo dance and dependency management for library maintainers while building a fat binary. Apple engineers also mentioned that you could bundle static libs with headers inside XCFramework. Xcode would automatically update your project headers search paths (which is convenient if you use third-party static libs).

During the session, Apple engineers described XCFramework and how to build it. There are three steps in a short build:

  • Enable the "Build Libraries for Distribution" build option, which makes Xcode generate public interfaces for your library
  • create a framework archive for each platform with "xcode archive."
  • run new xcodebuild command "xcodebuild-create-xcframework" passing as arguments archives from the previous step.

# Builds xcframework from iOS framework template project called TestFramework

# Archive for iOS
xcodebuild archive -scheme TestFramework -destination="iOS" -archivePath /tmp/xcf/ios.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES

# Archive for simulator
xcodebuild archive -scheme TestFramework -destination="iOS Simulator" -archivePath /tmp/xcf/iossimulator.xcarchive -derivedDataPath /tmp/iphoneos -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES

# Build xcframework with two archives
xcodebuild -create-xcframework -framework /tmp/xcf/ios.xcarchive/Products/Library/Frameworks/TestFramework.framework -framework /tmp/xcf/iossimulator.xcarchive/Products/Library/Frameworks/TestFramework.framework -output /tmp/xcf/TestFrame
work.xcframework

XCFramework is a bundle directory with a unique predefined structure. Most bundles contain an Info.plist file describing their properties, and the XCFramework is not an exception. I noticed a new value for 'CFBundlePackageType,' which was 'FMWK' for frameworks, and now Apple added 'XFWK' to denote XCFramework.

The rest of the file is a hierarchical description of frameworks contained inside. Each framework entry includes platform, architectures, and library identifiers - names of a directory inside the bundle where the corresponding framework resides.

Summary

XCFramework is a handy container for framework versions for different architectures and OSs. After the presentation of Catalyst, developers will have to build apps for even more platforms.

That's why the unified way of distributing your shared code seems reasonable. AppSpector SDK will soon be available as an XCFramework, so stay tuned.