Consider this directory tree from a vendor:
OwningTheLibs/
OwningTheLibs.a
include/
OwningTheLibs/
OwningTheLibs.h
module.modulemap
A vendor will probably hand you this and say "this supports iOS and iOS simulator". As of 2020 that 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"
}