Good old frameworks are dated back to time where there was only one platfrom and one language - Obj-C. Today we have multiple version of Swift and different platforms to support.
When XCFrameworks was introduced in last years WWDC I was really excited:
"XCFrameworks make it possible to bundle a binary framework or library for multiple platforms —including iOS devices, iOS simulators, and UIKit for Mac — into a single distributable .xcframework bundle that your developers can use within their own applications." 😲— Kamil Pyć (@KamilPyc) June 3, 2019
I would like to share my experience with it and what roadblocks you can hit trying to replace Frameworks with XCFrameworks in you project.
Note: all of code and examples was run on Xcode 11.4.1. using Swift 5.2.
Lets start from the begining - how to create XCFramework. With Frameworks that just a normal target workflow as you may know from creating app or test targets. Just select “New…” and pick Framework as target.
I would expect that from this point one can select which architectures to include and then export
.xcframework. But it doesn’t work this way.
In order to produce xcframework we need to export all of the frameworks and then combine them with
I think that this over complication can be one of the reasons why XCFrameworks are still not supported by Carthage.
On a first glance usage isn’t different than how we work with standard frameworks. Just add it to target’s “Frameworks and Libraries” section in general tab.
After building app in build log we can find out that XCFrameworks get different treatment than Frameworks.
There is a special
ProcessXCFrameworkLibrary step that’s extracts correct
.framework from all architectures contained in xcframework bundle based on target architecture.
Because of that Xcode need to have explicit list of xcframeworks to process and
FRAMEWORK_SEARCH_PATHS does not work.
Xcode wants to optimise the usage of
ProcessXCFrameworkLibrary and ther is only one call per single
xcframework. That’s will make build to fail in pretty common usage case.
Let’s say we have target
ModelB.framework that links same
ModelCore.xcframework. Xcode selects one of this targets to include the
ProcessXCFrameworkLibrary step and if this selected target will start after another target utilising same xcframework build will simply fail with
Framework not found ModelCore. I took me some time to figure out this race going on when my builds fails randomly 50% of time.
WWDC2020 update: Xcode 12 fixes the race condition bug:
In order to produce Swift libary that’s support module stability, frameworks needs to be created with
BUILD_LIBRARY_FOR_DISTRIBUTION turned on:
Under the hood this will pass
-enable-library-evolution flag to Swift Compiler. That have couple of implications:
- Code is compiled differently. When I tried to replace Apollo framework with precompiled xcframework version app started crashing with
- If you have any enums you either have to mark them
@frozenor handle default case on usage. Unfortunalty that make it impossible to simply replace
.frameworkin case any code needs to be debugged or profiled.
.swiftinterfacefile is corrupted. This was is already reported but not solved, so you cannot build SwiftyJSON or RxSwift as xcframework.
In summary, there are few things that can block developers from using XCFrameworks expecially with Swift projects. That could explain why they are not widely popular on iOS. We are one month ahead of WWDC and I hope those get issues got resolved, because XCFrameworks are great step ahead of Framework.