Holding true to their promise, Apple has open sourced the Swift programming language! You can read about it on the Swift language website, or peruse the source on GitHub. Snuggled in there, amongst the other goodies, is an unassuming project called “Swift Package Manager.”
That’s right, Swift now has an official package manager!
What is a Package Manager?
A package manager is a tool that simplifies the process of working with code from multiple sources. Specifically, it needs to:
- Define what a “package” actually is.
- Define how packages are accessed.
- Allow packages to be versioned.
- Manage dependencies.
- Link code to its dependencies.
What is a Swift Package?
To understand packages in Swift, you first need to understand that Swift is designed around the concept of “modules.” A module is a collection of source files that are compiled together as a unit. For an iOS app, that would be the files that make up the app. For a Swift package, the module contains package’s the source code files.
Modules are useful for defining packages because Swift has a special access control level called internal. An internal class (or struct, method, etc.) is available to the code in the same module, but cannot be used by code in other modules. That allows package authors to hide implementation details from the outside world while still easily accessing them internally.
Where do Swift Packages Live?
The Swift Package Manager is based on Git repositories. That means that any Git repo with the right contents can be used as a package. That repo could be hosted on a centralized service, like GitHub, or it could be hosted privately. It could even be stored locally on your laptop.
How are Swift Packages Versioned?
Package versions use the major-minor-patch scheme that’s common in modern systems. That means that there’s full support for semantic versioning.
The package manager uses tags in the package repo to identify versions. Publishing a new version of a package is as simple as pushing a new tag (e.g. “
How are Dependencies Managed?
Each package has a “manifest” file that contains metadata like its name and a list of its dependencies. For each dependency, the package provides a (Git) URL and an acceptable version range. A version range can be be pinned to a major version (e.g. “
1.x“), minor version (e.g “
1.1.x“) or even a specific patch version (e.g. “
If two packages depend on different versions of a third package, the package manager tries to find a version of that package that both will find acceptable. I mentioned earlier that package versions support semantic versioning. The package manager follows SemVer rules when selecting suitable package versions.
The manifest is actually a Swift source file, called “Package.swift”, which means dependencies are declared programmatically. That has the interesting side effect that package authors don’t need to memorize special text-based version specifiers, like “
~> 2.0.1“. Instead, they write Swift code like:
Version(2, 0, 1) ..< Version(2, 1, 0)
The source code for the Version class highlights some of the expressiveness of Swift as a language, and is a really interesting read. But, that’s a topic for another article.
How is a Swift Package Used?
Since a package is just a code module and some metadata, using a package is easy. Once the package has been installed, you can import its module, just like you might “
import Foundation” in an iOS or Mac app.
What does it all mean?
Although it already fulfills the core requirements of a package manager, the Swift Package Manager is still very young. The project documentation describes it as a “work in progress.” The most obvious short-coming at the moment is the lack of an official package registry. For now, the Swift Package Manager is a low-level tool for managing dependencies between code modules. It’s not hard to imagine a future, though, where finding new modules is as easy as it is with CocoaPods, NPM, Ruby Gems and other package managers.
If you’d like to learn more about the Swift Package Manager, the Swift.org project page has a good overview of how it works. And the Community Proposal does a great job of describing the motivations behind the project and its future goals.