Adding third-party dependencies

Now that we have learned the basics of using SPM to create a simple library, let's have a look at how to work with external dependencies. So, let's add one to our package. For this example, we will work with Google Promises, which we covered in Chapter 12, Futures, Promises, and Reactive Programming. So, open Package.swift in your preferred editor and make sure the dependencies section looks like this (the text you should add is in bold):

    dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/google/promises.git", .exact("1.2.3")),
],
When it comes to editors, every programmer has their inalienable preferences. Swift is supported by most text editors, including Sublime Text, Atom, and Emacs. You may find, though, that the level of Swift support varies across editors, and Xcode provides unequivocally the best Swift support. This state of things could be about to change, though. Indeed, the Swift team recently released their initial implementation of the Language Protocol Server for Swift. This is still a work in progress, but as it becomes more stable and rich in features, you might see that most editors will provide the same level of support for Swift, hopefully it's the same level as for Xcode. So, stay tuned!

After saving the file, run swift package resolve to get the dependency installed under .build/repositories/:

Fetching https://github.com/google/promises.git
Completed resolution in 1.81s
Cloning https://github.com/google/promises.git
Resolving https://github.com/google/promises.git at 1.2.3

The swift package resolve command also creates a Package.resolved file in your package root, which contains a list of all installed dependencies along with their repository URL, version, and commit hash. If you want to get a shortened version of the same information, just run swift package show-dependencies, which will show the following:

warning: dependency 'Promises' is not used by any target
.
└── Promises<https://github.com/google/promises.git@1.2.3>
SPM supports a number of different commands. If you want to get an overview, or if you forgot how a specific command is called or its arguments, you can run swift package --help to be reminded.

Now, we could import Promises in our executable main.swift file and refactor our code to use that library. Instead, we will leave that as an exercise to the reader and will focus on another important feature in SPM: its ability to automatically update dependencies when new versions are available.

In our current example, we pinned Google Promises down to version 1.2.3. Say we now want to remove that restriction and accept any version of that library starting with 1.2.3. To do that, we modify our Package.swift dependency section to look like this, where we replaced .exact("1.2.3") with from:"1.2.3":

    dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/google/promises.git", from: "1.2.3"),
],

After that, whenever you want to make SPM update your package dependencies, just run swift package update:

$ swift package update
Updating https://github.com/google/promises.git
Completed resolution in 1.14s
Resolving https://github.com/google/promises.git at 1.2.4

Besides specifying the exact version you want to use, or a starting version, SPM supports a number of additional possibilities:

.package(url: "https://github.com/google/promises.git",
.upToNextMajor(from: "1.2.3")),
.package(url: "https://github.com/google/promises.git",
.upToNextMinor(from: "1.2.4")),
.package(url: "https://github.com/google/promises.git",
"1.2.3"..<"1.2.6"),
.package(url: "https://github.com/google/promises.git",
"1.2.3"..."1.2.8"),
.package(url: "https://github.com/google/promises.git",
.branch("develop")),
.package(url: "https://github.com/google/promises.git",
.revision("e74b07278b926c9ec6f9643455ea00d1ce04a021"))

This is all great! But let's see how we can leverage SPM in a workflow aimed at the creation of a full-fledged iOS or macOS app written in Xcode.

If you ever get a sense that SPM is not doing what you expect it to, such as not updating a dependency after you changed its version number in Package.swift, the swift package reset command can come to your rescue. It will completely empty your package's build/cache directory and force SPM to get and build any dependencies from scratch.