ARTICLE AD BOX
In this dependency graph:
MyBinaryFramework -> CMyBinaryFramework -> MyLibrary
You can safely remove the CMyBinaryFramework dependency as it is unnecessary, Swift Package Manager supports binary target(xcframeworks). Your MyLibrary can use c/c++ public symbols from MyBinaryFramework directly if you provide module.modulemap in the framework.
Your MyBinaryFramework.xcframework's folder structure should look like this:
. ├── Info.plist ├── ios-arm64 │ └── libavformat.framework │ ├── Headers │ │ ├── avformat.h │ │ ├── avio.h │ │ ├── config.h │ │ ├── os_support.h │ │ ├── version_major.h │ │ └── version.h │ ├── Info.plist │ ├── libavformat │ └── Modules │ └── module.modulemap ├── ios-arm64_x86_64-simulator │ └── libavformat.framework │ ├── Headers │ │ ├── avformat.h │ │ ├── avio.h │ │ ├── config.h │ │ ├── os_support.h │ │ ├── version_major.h │ │ └── version.h │ ├── Info.plist │ ├── libavformat │ └── Modules │ └── module.modulemap ├── macos-arm64_x86_64 │ └── libavformat.framework │ ├── Headers -> Versions/Current/Headers │ ├── libavformat -> Versions/Current/libavformat │ ├── Modules │ │ └── module.modulemap │ ├── Resources -> Versions/Current/Resources │ └── Versions │ ├── A │ │ ├── Headers │ │ │ ├── avformat.h │ │ │ ├── avio.h │ │ │ ├── config.h │ │ │ ├── os_support.h │ │ │ ├── version_major.h │ │ │ └── version.h │ │ ├── libavformat │ │ └── Resources │ │ └── Info.plist │ └── Current -> A ├── tvos-arm64 │ └── libavformat.framework │ ├── Headers │ │ ├── avformat.h │ │ ├── avio.h │ │ ├── config.h │ │ ├── os_support.h │ │ ├── version_major.h │ │ └── version.h │ ├── Info.plist │ ├── libavformat │ └── Modules │ └── module.modulemap └── tvos-arm64_x86_64-simulator └── libavformat.framework ├── Headers │ ├── avformat.h │ ├── avio.h │ ├── config.h │ ├── os_support.h │ ├── version_major.h │ └── version.h ├── Info.plist ├── libavformat └── Modules └── module.modulemap 27 directories, 47 filesThis legal bundle structure is addressed in Placing content in a bundle, heads up:
MacOS uses a structure called versioned bundle to support multiple library versions. Using iOS's bundle structure on a macOS target allows you to run your app, but you can't finally archive it. It will be mentioned in below.
If your xcframework is correctly made, you don't need to wrap it with SPM. Just drag it into Xcode, and your swift code can use that framework.
Below are the steps I used to create a multi-platform xcframework from command line, I strongly recommend you to follow the steps:
Build for Each Platform/ArchitectureFirst, build your library for each platform and architecture combination, the library can be static(.a) or dynamic(.dylib):
# Build for each platform (iOS, macOS, tvOS, etc.) and architecture (arm64, x86_64) Create fat/universal libraries using lipo for each platform, including iOS, iOS Simulator, macOS, tvOS, and tvOS Simulator, among others.Combine architectures for each platform:
# Static library lipo -create \ ./build/ios-simulator/thin/arm64/lib/libmylib.a \ ./build/ios-simulator/thin/x86_64/lib/libmylib.a \ -output ./mylib/ios-simulator/libmylib.framework/libmylib # Or Dynamic library lipo -create \ ./build/ios-simulator/thin/arm64/lib/libmylib.dylib \ ./build/ios-simulator/thin/x86_64/lib/libmylib.dylib \ -output ./mylib/ios-simulator/libmylib.framework/libmylib Now we have a fat libmylib, we then need to Create Framework Bundle StructureFor iOS/tvOS (flat structure), they are just folders in this structure:
libmylib.framework/ ├── libmylib # The binary ├── Headers/ # Public headers │ └── *.h ├── Modules/ │ └── module.modulemap └── Info.plistFor macOS (versioned bundle structure, -> means symbolic link, create them with ln, e.g. ln -s Versions/Current/libmylib libmylib):
libmylib.framework/ ├── libmylib -> Versions/Current/libmylib ├── Headers -> Versions/Current/Headers ├── Resources -> Versions/Current/Resources ├── Modules/ │ └── module.modulemap └── Versions/ ├── A/ │ ├── libmylib # The actual binary │ ├── Headers/ │ │ └── *.h │ └── Resources/ │ └── Info.plist └── Current -> AYou can omit the mac platform if you don't need it.
Create module.modulemap(required if you need to export public symbols to swift)create that file with this content:
framework module libmylib [system] { umbrella "." exclude header "internal.h" # Optional: exclude private headers export * } Create Info.plistYou may need to modify the CFBundleSupportedPlatforms in each plist, they may be one of these values:
iPhoneOS: For applications or frameworks intended for iOS devices (actual iPhones, iPads). iPhoneSimulator: For applications or frameworks intended for the iOS Simulator. MacOSX: For applications or frameworks intended for macOS. AppleTVOS: For applications or frameworks intended for tvOS devices. AppleTVSimulator: For applications or frameworks intended for the tvOS Simulator. WatchOS: For applications or frameworks intended for watchOS devices. WatchSimulator: For applications or frameworks intended for the watchOS <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>libmylib</string> <key>CFBundleIdentifier</key> <string>com.example.libmylib</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>libmylib</string> <key>CFBundlePackageType</key> <string>FMWK</string> <key>CFBundleShortVersionString</key> <string>1.0.0</string> <key>CFBundleVersion</key> <string>1.0.0</string> <key>MinimumOSVersion</key> <string>13.0</string> <key>CFBundleSupportedPlatforms</key> <array> <string>iPhoneOS</string> </array> </dict> </plist> Create XCFramework with xcodebuildWith the libmylib.framework folder structure ready for each platform, time to create a XCFramework with xcodebuild
xcodebuild -create-xcframework \ -framework ./mylib/ios/libmylib.framework \ -framework ./mylib/iossimulator/libmylib.framework \ -framework ./mylib/macos/libmylib.framework \ -framework ./mylib/tvos/libmylib.framework \ -framework ./mylib/tvossimulator/libmylib.framework \ -output ./Sources/libmylib.xcframework Integrating with Swift Package ManagerFinally you can integrate with SPM, just put the xcframework in Sources folder
// swift-tools-version:5.9 import PackageDescription let package = Package( name: "MyLibrary", platforms: [ .iOS(.v13), .macOS(.v10_15), .tvOS(.v13) ], products: [ .library(name: "MyLibrary", targets: ["MyLibrary"]) ], targets: [ // Binary target for XCFramework .binaryTarget( name: "libmylib", path: "Sources/libmylib.xcframework" ), // Swift wrapper target (optional) .target( name: "MyLibrary", dependencies: ["libmylib"], linkerSettings: [ .linkedFramework("VideoToolbox"), .linkedFramework("CoreMedia"), .linkedLibrary("z"), .linkedLibrary("bz2") ] ) ] )Comment below if you need some help!
