Xcode and XCFrameworks — new format of packing frameworks

TrueEngineering.co
True Engineering
Published in
8 min readNov 21, 2019

--

In life of many companies, which have and develop their own stack of libraries (libs) and components, there comes a time when volumes of this stack cannot be easily supported.

In case of development for iOS platform, and in the whole for Apple ecosystem, there are two options how to link libs as dependencies:

1. To compile them each time while building the application.

2. To compile them in advance, using already built dependencies.

When choosing the second option, it becomes logical to use CI/CD systems for compilation of libs into ready-to-use artifacts.

However, the need to compile libs under several platforms or CPU architectures at Apple ecosystem requires performing nontrivial operations as when compiling the lib, as well as when building the product, which uses it.

Against that backdrop, it was difficult not to see and extremely interesting to explore one of Apple innovations, presented at WWDC 2019 within frameworks of Binary Frameworks in Swift presentation — format of packing frameworks — XCFramework.

XCFramework has several advantages in comparison with the established approaches:

1. Packing dependencies under all target platforms and architectures into one single bundle from the box

2. Connection of the bundle in the format of XCFramework, as a single dependency for all target platforms and architectures

3. Missing the need of building fat/universal framework

4. No need to get rid of x86_64 slices before uploading end applications to AppStore

In the article we will tell you why this new format was implemented, what it is, as well as what it gives to the developer.

How the new format appeared

Earlier, Apple launched the manager of dependencies — Swift Package Manager.
The fact is that Swift PM allows performing delivery of libs in the form of open source code with the description of dependencies.

From the developer’s position, delivering the lib, we would like to stress two aspects of Swift PM:

· Obvious minus — for various reasons, not all providers of libs would like to open their source code to customers.

· Obvious plus — while compilation of dependencies from sources, we get rid of the need to follow the binary compatibility of libs.

Apple presented XCFramework as a new binary format of packing the libs, considering it as an alternative for Swift Packages.

This format, as was the ability to connect the library, compiled in XCFramework, is available, starting from Xcode 11 and its beta versions.

What XCFramework is like

Inherently, XCFramework is a new way of packing and providing libs, in their different variants.

Except others, the new format also allows providing as packing statistics libs with their headline files, as well as libs written in С/Objective-C.

Let us look at the format in more details:

1. Packing dependencies under all target platforms and architectures in one single bundle from the box

All builds of the library under all target platforms and architectures now can be packed ino one single bundle with .xcframework extension.
However, for that, now, it is needed to use scripts for calling the command xcodebuild with the new key for Xcode 11 -create-xcframework.

The building and packing process will be considered below.

2. Connection of the bundle in the format of XCFramework, as a single dependency for all target platforms and architectures

Because the bundle .xcframework supports all required options for compilation of dependencies, we do not need to care about its architecture and the target platform.

At Xcode 11 the library, packed in .xcframework is linked in the same way, as a usual .framework.
More particularly, we can do this in the following ways in target settings:

· Adding .xcframework to the paragraph ‘Frameworks And Libraries’ on the tab ‘General’

· Adding .xcframework to ‘Link Binary With Libraries’ on the tab ‘Build Phases’

3. Missing the need in fat/universal build of the framework

Earlier for support of several platforms and architectures at the linked library, we had to prepare so called fat or universal frameworks.

It was performed with help of the command ‘lipo’ for sewing all variants of the compiled framework into unified fat binary.

In more details it is described, for instance, here — Writing Custom Universal Framework in Xcode 10.2 and iOS 12 (medium.com).

4. No need to get rid of x86_64 slices before uploading end applications to AppStore

Usually such a slice is used for providing operation of libs at iOS simulator.
When you try to download the application with dependencies, containing x86_64 slice, to AppStore, you can face with the famous error ITMS-90087.

Creation and packing XCFramework: theory

At mentioned earlier presentation, there are described several steps that are required for compiling and packing the lib in XCFramework format:

1. Project preparations

For a start at all target projects, which are responsible for compiling the lib under target platforms, it is needed to enable new for Xcode 11 setting — «Build Libraries for Distribution».

2. Building the project for target platforms and architectures

Further, we should to collect all targets for target platforms and architectures.
Let us show the examples of calling commands by the case of the concrete project configuration.

Let’s say that we have 2 schemes in the project — ‘XCFrameworkExample-iOS’ and ‘XCFramework-macOS’.

Additionally, two targets in the project compile the lib for iOS and for macOS.

For building all required configurations of the library we need to compile both targets, using the corresponding schemes. However, for iOS we need two builds: one for endpoint devices (ARM), and another one for the simulator (x86_64).

For that, we can use the command xcodebuild:

As a result, we have got 3 compiled frameworks which will be packed further to the container .xcframework.

1. Packing compiled .framework to .xcframework

It can be done with help of the following command:

Here can be many parameter indications -framework, which point at all builds .framework, that are to be included in .xcframework.

Preparation of the lib project for future building and packing XCFramework

TL;DR: ready project can be downloaded from Github repository.

As an example, let realize the lib which will be available for two platforms: iOS and macOS.
We can use the mentioned above project configuration: two schemes and two corresponding framework targets for iOS and macOS platforms.

The lib will provide the simple extension for String? (Optional where Wrapped == String), with the single attribute.

Let’s call this attribute isNilOrEmpty and, as it follows from the name, it will allow us to know when there is no value inside String? or the stored string is empty.

The code can be realized in the following way:

Let’s proceed with creation and configuration of the project.

1. For the start, we need to create the project ‘Framework’ for one of two target platforms on your choice: iOS or macOS.

You can do it at Xcode through menu item ‘File’ => ‘New’ => ‘Project’, or by keyboard shortcut ⇧ + ⌘ + N (by default).

As a next step, at the top of the dialogue we choose the needed platform (iOS or macOS), choose the project type ‘Framework’ and go to further by the buttonи ‘Next’.

On the next screen, we need to set the project name in the field ‘Product Name’.

As a variant, we can use the basic project name, in the mentioned earlier configuration this name is ‘XCFrameworkExample’.

In the future, when configuring a project, we will add suffixes to the basic name, indicating the platforms.

2. After that, we need to create one more Target by type ‘Framework’ in the project for another from the listed platforms (except that, which the project was created for, initially).

For that, we can use the menu item ‘File’ => ‘New’ => ‘Target’.

Further, we choose in the dialogue another one (relatively to the selected in the paragraph 1) from two platforms, and after that, we again choose the project type ‘Framework’.

For the field ‘Product Name’ we can use the name with the suffix of the platform, which we add the target for. So, if the platform is macOS, then its name can be ‘XCFrameworkExample-macOS’ (%base_name%-%platform%).

3. Setting up targets and schemes, to make them easier for difference.

For the start, let rename our schemes and the linked targets in the way, when their names reflect the platforms, for instance:

· ‘XCFrameworkExample-iOS’

· ‘XCFrameworkExample-macOS’

4. Further, we add .swift file with the code of our extension for String? to the project.

Let’s add .swift file with the name ‘Optional.swift’ to the project.
And, earlier mentioned extension for Optimal is to be put to the file itself.

Important! No to forget to add the file with the code to both targets.

Now we have created the project, which can be built in XCFramework, using commands from the previous stage.

Process of building and packing the lib in the format .xcframework

For building and packing the lib to the format .xcframework at this stage, we can use bash script, at the separate file. Additionally, it allows using these practices in the future, for integration of the solution into CI/CD systems.

The script looks simple and, in fact, brings together earlier mentioned commands for building process:

Content of .xcframework

At the result of the build script working from the previous paragraph of this article, we have the needed bundle .xcframework, which can be added to the project.

If we look inside this bundle, which, as and .framework, is in fact simple folder, we will see the following structure:

Here we can see that inside .xcframework there are builds in the format .framework, broken by platforms and architectures. Also for description of the bundle .xcframework content, there is the file Info.plist inside.

The file Info.plist has the following content:

We can notice that for the key ‘CFBundlePackageType’, unlike the format .framework, using the new meaning ‘XFWK’, but not ‘FMWK’.

Results

Thus, the format of packing libs in XCFramework is the simple container for libs compiling in the format .framework.

However, this format allows saving separately and using independently every from the architectures and platforms, presented inside. It gets rid of the issues, connected with the widespread approach with the building fat/universal frameworks.

Howbeit, now there is the important nuance concerning the question of using XCFramework in real projects — it is managing the dependencies that have not been yet realized at the XCFramework by Apple Company.

For these purposes usually Swift PM, Carthage, CocoaPods atre used and other systems of managing dependencies and their building. That’s why it is not a surprise that support of the new format is being realized right now, especially for projects CocoaPods and Carthage.

--

--