Most of the code we write depends on external libraries. Libraries are pieces of code that can be used by many applications. The OS already comes with essential libraries we can use as building blocks, and all we have to do is import
their modules. This is what we do when we need libraries like Foundation
, SwiftUI
, or UIKit
. But what happens when we need to use a library that doesn’t come with the OS?
In this case, we can manually download the library and embed it in our project. This process gets complicated if we depend on multiple libraries, though. A library might also depend on other libraries, which means we would need to manually manage each sub dependency. Another problem we might face is dealing with versions. How do we know when to update a specific dependency?
Those problems are what make dependency managers exist. They automate those processes, making our lives simpler and allowing us to focus on writing code. SPM is Apple’s dependency manager and is part of the Swift project. It integrates with the Swift build system to provide us the full experience of downloading, compiling, linking, and updating dependencies. Xcode also integrates with it, exposing controls to use this tool.
Packages
SPM works by managing packages. A package is a flexible container of source code that can distribute libraries to clients. In this article, we’ll be using a simple open-source package called Time. More specifically, we’ll explore how we can use SPM to use this library in our code.
The examples in this article use Xcode 13.2.1. If you wish to follow along, create a new iOS project using Swift (the UI framework doesn’t matter).
Declaring Dependencies
To use a package in our iOS app, we first need to declare it as a dependency in the Package Dependencies screen:
- In the navigation area, select the project
- At the left bar, select the project again (not the target)
- At the top tab bar, select the Package Dependencies tab

Now we need to add the Time package:
- Copy the repository URL: https://github.com/davedelong/time.git
- Select the + button, under Packages
- Paste the copied URL in the search bar at the top
- In the dependency rule option, select “Up to Next Major Version”

Xcode will then fetch this package and ask us which library we want to use (one package can distribute multiple libraries). This package is simple, it only distributes one:

Once we add this package to our project, Xcode will display it in the Package Dependencies list, in the navigation area:

Package Resolution
After we declare the dependencies in our project, SPM will resolve them:
- Look at the URL and version of each package
- Fetch each dependency with the correct version
- Repeat this process for each sub dependency of a package if it has any
- Configure the packages with the project, allowing us to import their distributed libraries
After the package resolution phase, SPM generates a JSON file called Package.resolved
. It contains which packages were resolved and what their versions are. SPM uses it to figure out whether a dependency needs to be updated or not. Here’s what this file looks like:
{
"object": {
"pins": [
{
"package": "Time",
"repositoryURL": "https://github.com/davedelong/time.git",
"state": {
"branch": null,
"revision": "be6cbbbb97aa4570e3b51bd56f98ca3cf62aa3cb",
"version": "0.9.2"
}
}
]
},
"version": 1
}
Package.resolved
is located in the swiftpm
folder, inside your xcodeproj
file:
SPM-Basics.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
It’s a good idea to put the swiftpm
folder under version control. This way we can ensure every team member is using the right package versions.
Packages Location
The resolved packages are located in the derived data folder. A lot of times developers have problems with Xcode, and deleting this folder sometimes solves those issues. Just be aware that when you delete it, you’ll also delete the cached dependencies, forcing a new resolution of packages.
The Three SPM Actions
Once we have the packages in place, we can use three different actions for dealing with them:

Reset Package Caches
Use this action to erase the cached packages of our project from the derived data folder. SPM will automatically resolve the dependencies again.
Resolve Package Versions
Use this action to force a new resolution of dependencies. If your package.resolved
file was updated by another team member, this action will ensure your local packages match the versions in that file.
Update to Latest Package Versions
The action name says it all. It will check if any dependency has available updates and will update them according to the dependency rule (e.g Up to next major). Semantic versioning is used to organize the package versions.
Be careful
Packages can come from third-party developers. Before using these packages, ensure the following:
- You understand what the package does and what sub dependencies it uses
- It comes from a trusted source
- If it’s an open-source package, ensure it’s maintained by the community (check the latest changes, number of stars, and so on)
- The package license allows your application to use it
Conclusion
In this article, we’ve explored how SPM works. Here’s what we learned:
- What SPM and packages are
- How to add packages to an Xcode project
- What package resolution is
- Where Xcode stores the resolved packages
- How to manage the packages in Xcode
- What to take into account when adding a package
In the upcoming article, we’ll explore how we can create a Swift package and what it looks like in terms of structure.