Using Swift Package Manager to distribute a 3rd-party binary framework
Recently, I had to integrate a dependency that wasn’t available via Swift Package Manager (SPM), which is my preferred dependency manager these days. The options available were CocoaPods and manual installation. I didn’t want to add CocoaPods for a for a single dependency, so that leaves manually integrating it. That typically involves manually downloading the framework and vendoring it into your repo, which I hate doing for a bunch of reasons:
- Adds bloat to your repo, depending on how big the framework is
- Makes it harder to track and manage which version of the dependency you’re using
- Every time you have to update it, you have to track down the new link, download it again, delete the old one, and make a noisy diff when you really just want to bump versions.
It’s not the end of the world, but it’s a pain and this time I thought there has to be a better way, and luckily, there is! As of Swift 5.3, SPM supports binary targets. So that means all we need to do is make our own Package.swift
with a target that points the 3rd-party’s binary framework. I had to do this recently with Amazon’s IVS SDK, and this was how I solved it:
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "AmazonIVSPlayer",
products: [
.library(name: "AmazonIVSPlayer", targets: ["AmazonIVSPlayer"])
],
dependencies: [],
targets: [
.binaryTarget(
name: "AmazonIVSPlayer",
url: "https://player.live-video.net/1.7.0/AmazonIVSPlayer.xcframework.zip",
checksum: "39c8b9f9423b0105d3e10b20a4cf3ebb72e7f5fd77ce4bdac68a6c6aa8b4a42a"
)
]
)
You still have to do a little bit of work to update this package and tag the version when the vendor updates the framework, but I still prefer that over needing to manually manage it, since it means you get to treat it like all of your other dependencies. You can easily switch between versions in different branches, revert to older versions if there is a bug, etc. I’m unsure of the legality around creating a package to distribute someone else’s library, so I didn’t make this package public for others to consume.
One gotcha I encountered is SPM wants the binary target’s name to match the name of .xcframework
provided, so make sure they are the same. If the vendor doesn’t provide the checksum on their site, you can easily compute the sha256 checksum on macOS with shasum -a 256 path/to/archive.zip
Hopefully, this is becoming a problem of the past now that SPM has support for resources and binary targets. I think it’s capable of covering the majority of use cases these days and it slowly seems to becoming the de facto means of distributing iOS and macOS libraries, but it’s still a nice tool to have in your toolbox.