You can think of an xcframework
as a sumo "binary".
Here's how I understand things after going though this a few times:
- An
xcframework
is for bundling multiple platforms together into a distributable binary package. You can put ios
, ios-simulator
, ios-macabi
, darwin
(macos), and tvos
(and watchOS and carPlayOS and their respective simulator) platform code together into an xcframework
. Those are your "library definitions". So the maximum number of frameworks (.framework
) or libraries (.a
) that can go in an xcframework
at the top level is limited to the total number of platforms. And they all must be targeting different system interfaces (platforms) which means you can't put two binaries expecting e.g. the ios-simulator
system interface into an xcframework
at the top level. - In order to support the different architectures on each platform, you must
lipo
those architecture slices together to make a fat binary. Generally the options for those, on modern Apple hardware, are x86_64
, and arm64v8
(aarch64). You may also need armv7
and i386
if you're targeting older hardware. The same rule goes for lipo
, you can only have one instance of any given architecture slice in a fat binary which means you can't put two binaries targeting e.g. x86_64
hardware into a fat binary at the top level.
There is some literature suggesting that lipo
is not an Apple tool and is not supported (3rd and 4th bullet point under Motivation & consequences section: https://awesomeopensource.com/project/bielikb/xcframeworks). I believe that to be inaccurate. lipo
is distributed with Xcode, by Apple:
❯ lipo
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: ...
...
That resource is otherwise somewhat helpful and much appreciated. I believe the misunderstanding is that Xcode now does the heavy lifting so that you are no longer manually required to add special build phases and invoke lipo
yourself anymore (unless you're not using Xcode, of course).
Look at the folders in the xcframework
(other folders omitted for brevity):
❯ tree
.
└── Foo.xcframework
├── Info.plist
├── ios-arm64
│ └── libfoo-ios.a
├── ios-arm64_x86_64-maccatalyst
│ └── libfoo-ios-macabi.a
├── ios-arm64_x86_64-simulator
│ └── libfoo-ios-sim.a
└── macos-arm64_x86_64
└── libfoo-macos.a
Clearly Xcode is combining architecture slices when building frameworks that target e.g. ios-simulator
. So this strategy seems to be both supported and the intended way to distribute a framework that can be used with multiple architectures.