Consider this directory tree from a vendor:
OwningTheLibs/
OwningTheLibs.a
include/
OwningTheLibs/
OwningTheLibs.h
module.modulemap
The vendor will probably say "this supports iOS and iOS simulator". As of 2019/2020 this is no longer true, but we don't need to get in to that right now.
Run the lipo tool on the .a
to get a check for its contents:
~ lipo -info OwningTheLibs/libOwningTheLibs.a
Architectures in the fat file: OwningTheLibs/libOwningTheLibs.a are: x86_64 arm64
If you see Non-fat file: OwningTheLibs/libOwningTheLibs.a
instead, stop.
You cannot proceed.
Thin the binary using lipo
:
~ mkdir -p simulator
~ lipo -thin x86_64 libOwningTheLibs.a -output simulator/libOwningTheLibs.a
~ mkdir -p device
~ lipo -thin arm64 libOwningTheLibs.a -output device/libOwningTheLibs.a
Create the xcframework:
$ xcodebuild -create-xcframework \
-library simulator/libOwningTheLibs.a \
-headers include \
-library device/libOwningTheLibs.a \
-headers include \
-output OwningTheLibs.xcframework
Note: If the vendor gave you debug symbols or Bitcode maps — which is unlikely, let's be honest - add
-debug-symbols
after any-headers
. It will need to be repeated multiple times. Seexcodebuild -create-xcframework -help
for more details.
The resulting xcframework will look like this:
OwningTheLibs.xcframework/
ios-x86_64-simulator/
Headers/
OwningTheLibs/
OwningTheLibs.h
module.modulemap
libOwningTheLibs.a
ios-arm64/
Headers/
OwningTheLibs/
OwningTheLibs.h
module.modulemap
libOwningTheLibs.a
The resulting xcframework can be dragged into Xcode into the "Frameworks, Libraries, and Embedded Content" of a target, or embedded into a Swift package:
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "OwningThePackages",
platforms: [ .iOS(.v14) ],
products: [
.library(
name: "OwningThePackages",
targets: ["OwningThePackages"]),
],
targets: [
.target(
name: "OwningThePackages",
dependencies: ["OwningTheLibs"]),
.binaryTarget(
name: "OwningTheLibs",
path: "OwningTheLibs.xcframework")
]
)
Use the following template:
module OwningTheLibs {
umbrella header "OwningTheLibs.h"
export *
module * { export * }
}
Drop that into the include/OwningTheLibs
directory to enable import OwningTheLibs
in Swift.
If the library is less like an ObjC project and more like a C project, you may have multiple header
entries instead:
module OwningTheLibs {
header "one.h"
header "two.h"
export *
module * { export * }
}
If the vendor says things like "you must link against SystemConfiguration
" or "you must link against libz
", you can also do that:
module OwningTheLibs {
umbrella header "OwningTheLibs.h"
export *
module * { export * }
link "z"
link framework "SystemConfiguration"
}