Binary Frameworks as Swift Packages
Swift Package Manager is a built-in Xcode tool for managing and distributing third-party dependencies. It recently gained the ability to distribute binary frameworks as Swift packages, which enabled us to offer PSPDFKit 10 for iOS as a Swift package.
In this article, we’ll cover both how to integrate a binary Swift package into your project and how we created a binary Swift package using Xcode 12. We’ll also discuss how Swift Package Manager compares to the other dependency managers for iOS like CocoaPods and Carthage.
In the next section, we’ll see how we integrated the PSPDFKit binary package into a simple sample project. So let’s get started!
Integrating a Binary Swift Package into Your Project
Adding a binary Swift package into your project can be done directly in Xcode in a few simple steps:
Go to Files > Swift Packages > Add Package Dependency… and include the link of the package dependency you want to add. In most cases, it’s the URL of a GitHub repository. In this tutorial, we’ve used the following URL to integrate PSPDFKit:
Select additional options such as the specific version, etc.
Wait for Xcode to finish downloading and resolving the Swift package into your project.
Choose the package products and targets.
You can integrate the PSPDFKit Swift package into your app, or you can download our sample project here to experiment.
Now that we’ve shown how to add a binary Swift package to a sample project, let’s see how we can build and distribute it.
How to Build and Distribute a Binary Swift Package
To create a binary Swift package, you need Xcode 12 and the binary framework you want to distribute. In this section, we’ll go over how we created the Swift package for both
Building the XCFramework
Binary Swift packages only support the XCFramework format, and fortunately for us, PSPDFKit was already distributed as an XCFramework for the manual and CocoaPods integrations.
To learn more about XCFrameworks and how to build them, please refer to our Supporting XCFrameworks blog post.
Creating a Swift Package in Xcode
A Swift package consists of the manifest file (
Package.swift), source code, binaries, and resource files. In our case — for the PSPDFKit Swift package — we only need the manifest, README files, and our binary frameworks.
First, we create the new Swift package in Xcode (File > New > Swift Package…), which will create a folder that has a structure similar to a regular Xcode project.
Then, we need to configure our manifest file like so:
// swift-tools-version:5.3 import PackageDescription let package = Package( name: "PSPDFKit", products: [ .library( name: "PSPDFKit", targets: ["PSPDFKit", "PSPDFKitUI"]), ], targets: [ .binaryTarget( name: "PSPDFKit", url: "https://customers.pspdfkit.com/pspdfkit/xcframework/10.0.0.zip", checksum: "bfb412ada4d291e22542c2d06b3e9f811616fb043fbd12660b0108541eb33a3c"), .binaryTarget( name: "PSPDFKitUI", url: "https://customers.pspdfkit.com/pspdfkitui/xcframework/10.0.0.zip", checksum: "4903f4b7e753ac4760a827a72d7ed836a29e1700218ddfaa4e1f70814bd6f085"), ] )
Now, let’s discuss the key aspects of the snippet above.
The Manifest File
Package.swift file describes our Swift package. It’s like the manifest file in other development environments such as Android, or the equivalent of the podspec file for CocoaPods. It’s the only file a binary Swift package requires. In
Package.swift, we describe the binary Swift package by instantiating a package object with its name,
Swift Tool Version
The comment at the top of the file is important because the Swift tool version represents the minimum version of Swift required to use the package.
The package name represents the name of your package library, and it doesn’t necessarily need to match your binary framework’s name or its module name.
products property describes the library that will be added to your Xcode project. In this case, we’re adding the
PSPDFKit library, which has two targets for each of the PSPDFKit XCFrameworks:
PSPDFKitUI. The target’s name needs to match the framework’s module name.
targets property is an array that defines the Swift package binaries.
We create a
binaryTarget by specifying once again the module name of each binary framework, the HTTPS download URL from which Swift Package Manager will download the binary, and the checksum.
ℹ️ Note: While it’s possible to check the binaries into the git repository, we decided to host the binaries on our own servers because Apple doesn’t recommend checking the binaries into the repository. This is to prevent git checkout slowdowns.
Binary Swift packages require the checksum for security purposes. Xcode will verify that the downloaded binary is the same one the vendor intended to bundle. To get the checksum of our binary frameworks, we use the
swift package compute-checksum command like so:
swift package compute-checksum path/to/PSPDFKit.xcframework.zip db3a6859475df74e11d98c8366c4cbf57055478d1ba21d39bc9771ad0af5a7f4
Distributing the Swift Package
Xcode expects Swift packages to be hosted and distributed as git repositories. We’re hosting our Swift packages for PSPDFKit, Instant, and PSPDFKitOCR on GitHub, and you can add them to your project using the URLs below.
How Does Swift Package Manager Compare to Other Dependency Managers?
Over the years, the iOS community has benefited from Carthage and CocoaPods. Swift Package Manager is the newest dependency manager for iOS developers, and it’s not as widely used as CocoaPods and Carthage. It also lacks certain features like CocoaPods’ subspecs and resource handling, to name a few.
For example, the main limitation Swift Package Manager has for our use case compared to CocoaPods is the ability to conditionally include resources (FB7787327). As a result, our customers have to manually integrate the desired language bundles. In CocoaPods, this can be easily achieved via subspecs.
Even though Swift Package Manager is open source, it’s harder to submit and discuss issues, especially if they’re related to the Xcode UI. It’s advantageous to use a fully open source dependency manager like CocoaPods or Carthage because you’ll be able to contribute and add new features. We at PSPDFKit have benefited from this, and in turn, we made the decision to sponsor CocoaPods.
Swift Package Manager has many pros compared to Carthage and CococaPods. For example, it’s built directly into Xcode, so there’s no need to install any additional tools or make sure the entire team updates at the same time. It’s also less likely to break new versions of Xcode or iOS.
Despite its limitations, Swift Package Manager is a great tool for managing iOS dependencies. While it’s not as mature and as feature-rich as Carthage and CocoaPods, we think Swift Package Manager will gain traction in the coming years.