Xcode 13.3 supports SPM binary dependency in private GitHub release
In this blog post I will share details about a new feature not mentioned in the Xcode 13.3 Beta 3 release notes. This feature is of interest to developers who need to make their code available as binaries to protect their intellectual property when developing proprietary, closed-source libraries.
To distribute code in binary form as a Swift package, create an XCFramework bundle that contains the binaries. Then, make the bundle available locally or on a server. Here is an example of a Package.swift
manifest to distribute a binary framework stored on a server:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.macOS(.v10_14), .iOS(.v13), .tvOS(.v13)
],
products: [
.library(
name: "MyLibrary",
targets: ["SomeRemoteBinaryPackage"])
],
dependencies: [],
targets: [
.binaryTarget(
name: "SomeRemoteBinaryPackage",
url: "https://url/to/some/remote/xcframework.zip",
checksum: "The checksum of the ZIP archive that contains the XCFramework."
)
]
)
The xcframework.zip file needed to be publicly accessible on a server. But what if you need to limit public access to authenticated users?
The .netrc
file format is commonly used for the automatic authentication of HTTP requests.
machine <example.com> login <my-user-name> password <my-password>
It generally resides in the userβs home directory (~/.netrc
). Learn more about netrc by reading the gnu documentation.
My former co-worker Stan Stadelman introduced netrc
into SPM to support basic auth aiming non-git binary dependency hosts.
The user is not required to opt-in to use netrc as the option is turned on per default in the Swift Package Manager.
/// Whether to load .netrc files for authenticating with remote servers
/// when downloading binary artifacts or communicating with a registry.
@Flag(inversion: .prefixedEnableDisable,
exclusivity: .exclusive,
help: "Load credentials from a .netrc file")
var netrc: Bool = true
SPM will automatically detect the .netrc
file and apply the required Authentication
header. Also, with option netrc-file <netrc-file>
you can specify a different location.
This feature became available with Xcode 12.5. However, the community pointed out that this does not work for a private GitHub release.
First, you cannot use the browser download URL as an HTTP request results in a 302 redirect. You have to lookup the asset name and then use the asset url.
- Incorrect URL:
https://github.com/:owner/:repo/releases/download/:tag/some.xcframework.zip
- Correct URL:
https://api.github.com/repos/:owner/:repo/releases/assets/\(assetId).zip
But more importantly, there is another issue that SPM was not aware of.
// netrc support is NOT enough to make this work for a private repo asset
.binaryTarget(
name: privateName,
url: "https://api.github.com/repos/:owner/:repo/releases/assets/\(assetId).zip",
checksum: checksum
)
The Github API states the Accept
header of the request to application/octet-stream
is required to download the asset's binary content. Otherwise the API JSON response is returned.
Hence the following change was introduced in SPM, by Jimmy Arts, to set the header automatically:
Xcode 13.3 Beta 3 includes this change. Hence, using Xcode 13.3 Beta 3 and a .netrc
file with a GitHub personal access token will finally allow you to download binary assets from release in a private GitHub repository. ππ
Example: I have a repository MyPrivateRepo
under my GitHub user MarcoEidinger
and I created release 1.0.0
with asset MyBinaryModuleName.xcframework.zip
. Hence the Package.swift
would be like
.binaryTarget(
name: "MyBinaryModuleName",
url: "https://api.github.com/repos/MarcoEidinger/MyPrivateRepo/releases/assets/58858051.zip",
checksum: "c533f08210ac21def782d48c2ff1e1a538b05b051e128aa88ffcd44051ddc2b3"
),
and my ~/.netrc
file would be something like
machine api.github.com login MarcoEidinger password ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Netrc is a great solution which works on Mac and also on Linux. If you are exclusively working on Mac then you can leverage Apple's Keychain as alternative. Instead of creating an entry in the ~/.netrc
file you can create an internet password containing
Keychain Item Name
: api.github.comAccount Name
: GitHub user namePassword
: personal access token
Xcode (SPM) will read from keychain once you granted permission. Such a dialogue may come up during package resolution.
P.S.: The whole history can be traced in the following thread in the Swift Forum: