This year at WWDC Apple introduced a new code distribution format – XCFrameworks, obviously, as a SDK developers we were excited about it! Distributing your code as a library has some issues related to Apple ecosystem and tech stack. For example, we can’t include Swift code in our library, we have to think about our dependencies and iOS simulator slice when our users upload their apps to the AppStore. Once I’ve looked through session 416, I decided to give it a try and build AppSpectorSDK as a XCFramework. Before going through the steps of building an XCFramework let’s quickly recap what the framework actually is.
Two main words you probably hear often while talking about libraries are static and dynamic. These terms refer to the way library code loaded and linked to the main process. In the case of a static library – it’s copied to the app binary at compile time and for a dynamic library – loaded at runtime during startup or triggered by the first reference to symbol inside it (weak loading). Dynamic frameworks are often distributed in a form of frameworks on Apple platforms. Framework is a type of bundle which is actually just a directory with a certain structure. In case of a framework it contains library code and plist file with some meta info about the framework. In general, frameworks usage is the preferred way to ship your code due to various reasons such as smaller binary size and easier updates. Most code you use from Apple SDKs is provided as frameworks loaded from the OS you are running on so you don't need to ship them with your app.
New framework format
A couple of weeks ago we were introduced with a new framework format – XCFramework, which is actually another type of bundle and it is very similar to framework but now containing code for multiple architectures and platforms. This means you can ship slices for any of the architectures, including simulator, any Apple OS and even separate slices for UIKit and AppKit apps. Obviously, Apple came up with this to make porting the code to macOS easier and getting rid of lipo dance and dependency management for library maintainers while building a fat binary. Apple engineers also mentioned you can bundle static libs along with headers inside XCFramework and Xcode would automatically update your project headers search paths (which is convenient in case you are using third party static libs).
During the session Apple engineers described what XCFramework is and how to build it. There are three steps in short building requires:
- enable “Build Libraries for Distribution” build option which makes Xcode generate public interfaces for your library
- make framework archive for each platform with “xcode archive"
- run new xcodebuild command “xcodebuild -create-xcframework” passing as arguments archives from 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
Let’s take a brief look at XCFramework. Framework is a special case of a bundle - directory with a special predefined structure. Most bundles contains Info.plist file describing their properties, and the XCFramework is not an exception. First thing I’ve noticed is a new value for ‘CFBundlePackageType’ which was ‘FMWK’ for frameworks and now Apple added ‘XFWK’ to denote XCFramework.
Rest of the file is hierarchical description of frameworks contained inside, each framework entry includes platform, architectures and library identifier, which is a name of a directory inside the bundle where corresponding framework resides.
You can see that nothing magical happens here, XCFramework is just a handy container for a framework versions for different architectures and OSs. After presentation of Catalyst, developers will have to build apps for even more amount of platforms in the near future, supporting macOS, that’s why the unified way of distributing your shared code seems perfectly reasonable. And of course AppSpector SDK soon will also be available as a XCFramework. Stay tuned!