Swift Package Manager
Reviewed & published by Brayan K
SwiftPM is the official way to organise code and pull in libraries. You'll read a Package.swift manifest, understand targets and products, declare dependencies, and add packages in Xcode and from the terminal.
Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Read and write a Package.swift manifest
- • Tell targets (modules of code) from products (what you ship)
- • Declare external dependencies with version rules
- • Wire a dependency into a target with .product(name:package:)
- • Build, test, and run with swift build / swift test
- • Add packages in Xcode via the repo URL
📊 Manifest Vocabulary
| Term | Meaning |
|---|---|
| Package.swift | The manifest (Swift code) |
| target | A module that gets built |
| product | A library/executable to share |
| dependencies | External packages you rely on |
| .testTarget | A target holding tests |
1️⃣ The Package.swift Manifest
The manifest is real Swift. It names the package, lists products (what consumers use) and targets (modules of code that build them).
// swift-tools-version:5.9
import PackageDescription
// Package.swift is the manifest — it's REAL Swift code, not a config file.
let package = Package(
name: "Utilities",
products: [
// A product is what others can use: a library or an executable.
.library(name: "Utilities", targets: ["Utilities"]),
],
targets: [
// A target is a module of code that gets built.
.target(name: "Utilities"),
.testTarget(name: "UtilitiesTests", dependencies: ["Utilities"]),
]
)Your turn. Fill in the product and target factory methods.
// 🎯 YOUR TURN — replace each ___ then build with 'swift build'.
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "Greeter",
products: [
.___(name: "Greeter", targets: ["Greeter"]), // 👉 a reusable library
],
targets: [
.___(name: "Greeter"), // 👉 a code module
]
)
// ✅ Once filled in, 'swift build' compiles a Greeter library.2️⃣ Dependencies
List external packages in the dependencies array with a version rule, then give a target access by naming the dependency's product.
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyApp",
dependencies: [
// List external packages here, pinned to a version rule.
.package(url: "https://github.com/example/Networking.git",
from: "1.2.0"),
],
targets: [
.target(
name: "MyApp",
// A target gains access by naming the dependency's product.
dependencies: [
.product(name: "Networking", package: "Networking")
]
),
]
)Now you try. Fill in the array that lists external packages.
// 🎯 YOUR TURN — add a dependency.
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "App",
___: [ // 👉 array that lists external packages
.package(url: "https://github.com/x/Logger.git", from: "2.0.0"),
],
targets: [
.target(name: "App", dependencies: [
.product(name: "Logger", package: "Logger")
]),
]
)
// ✅ Once filled in, App can 'import Logger'.3️⃣ Building from the Terminal
SwiftPM ships with the toolchain. Build, test, and run a package with a few commands — no Xcode required.
# From the package's root folder, in your terminal:
swift build # compile the package
swift test # run the test targets
swift run # build and run an executable product
# Create a brand-new package skeleton:
swift package init --type libraryPro Tips
- 💡 Keep the tools version current at the top of the manifest to unlock newer features.
- 💡 Pin sensible version rules like from: so updates stay within a major version.
- 💡 Split large code into multiple targets for cleaner module boundaries.
- 💡 Commit Package.resolved so teammates get the exact same dependency versions.
Common Errors (and the fix)
- "No such module" — you added the dependency but didn't list it in the target's dependencies.
- Manifest won't parse — the // swift-tools-version line is missing or malformed.
- Version resolution fails — incompatible version rules across packages; relax or align them.
- Tests don't run — the test target isn't declared with .testTarget or lacks a dependency on the code target.
📋 Quick Reference
| Task | Code | Notes |
|---|---|---|
| Library product | .library(name:targets:) | to share |
| Code module | .target(name:) | builds source |
| Tests | .testTarget(name:dependencies:) | unit tests |
| Dependency | .package(url:from:) | external pkg |
| Use it | .product(name:package:) | per target |
| Build | swift build | compile |
Mini-Challenge: Write a Manifest
Author a Package.swift for a MathKit library with a test target, then build it.
// 🎯 MINI-CHALLENGE: Write a package manifest
// Create a Package.swift for a package named "MathKit" that:
// 1. Declares swift-tools-version:5.9 and imports PackageDescription.
// 2. Exposes a .library product "MathKit" built from a "MathKit" target.
// 3. Has a .testTarget "MathKitTests" depending on "MathKit".
// 4. Bonus: add a dependency on a fictional package and wire it into MathKit.
//
// Then run 'swift build' to confirm it compiles.
// your manifest here🎉 Lesson Complete!
- ✅ Package.swift is the manifest — written in Swift
- ✅ Targets build code; products share it as libraries or executables
- ✅ List external packages in dependencies with version rules
- ✅ Give a target access via .product(name:package:)
- ✅ Build with swift build, or add packages in Xcode by URL
Practice quiz
Which file defines a Swift package?
- Info.plist
- Package.swift
- project.pbxproj
- package.json
Answer: Package.swift. Package.swift is the manifest that describes a Swift package.
The Package.swift manifest is written in...
- JSON
- YAML
- Swift
- XML
Answer: Swift. The manifest is real Swift code using the PackageDescription API.
What does a 'target' represent in a package?
- A build setting
- A module of source code (or tests) that is built
- A remote server
- A device
Answer: A module of source code (or tests) that is built. A target is a basic building block: a module of code that gets compiled.
What does a 'product' expose?
- A library or executable made from targets, usable by others
- A test result
- A storyboard
- A provisioning profile
Answer: A library or executable made from targets, usable by others. Products are libraries or executables that targets build into for consumers.
Where do you list external packages your package relies on?
- In targets only
- In Info.plist
- In a .gitignore
- In the dependencies array
Answer: In the dependencies array. The dependencies array of the manifest lists external packages.
How does a target gain access to a dependency?
- Automatically
- By naming it in that target's dependencies
- Via Info.plist
- It cannot
Answer: By naming it in that target's dependencies. A target must list the dependency in its own dependencies to import it.
Which command builds a package from the terminal?
- swift compile
- swift make
- swift build
- spm build
Answer: swift build. swift build compiles the package using SwiftPM.
How do you usually add a package to an app in Xcode?
- File > Add Package Dependencies and paste the repo URL
- Drag a .zip in
- Edit Info.plist
- Copy source files manually
Answer: File > Add Package Dependencies and paste the repo URL. Xcode adds packages via File > Add Package Dependencies using the repo URL.
Specifying .upToNextMajor(from: "1.2.0") controls...
- The Swift version
- The deployment target
- The product name
- The allowed dependency version range
Answer: The allowed dependency version range. Version rules like upToNextMajor pin which dependency versions are allowed.
Test targets are typically declared with...
- .executableTarget
- .testTarget
- .binaryTarget
- .systemLibrary
Answer: .testTarget. .testTarget declares a target containing unit tests.
Continue this course
- Previous: Unit Testing with XCTest
- Next: App Architecture with MVVM — Model-View-ViewModel, observable view models and testable design
Frequently asked questions
What is Package.swift?
Package.swift is a package's manifest: the single file that describes its name, products, targets, and dependencies. It is not a static config file but real Swift code that uses the PackageDescription API, so you build up a Package value programmatically. The first line declares the Swift tools version, which tells SwiftPM which manifest features are available.
What is the difference between a target and a product?
A target is the basic unit of code SwiftPM compiles: a module of source files, or a test target. A product is what the package exposes to consumers, built from one or more targets, and is either a library you import or an executable you run. In short, targets build the code internally, and products publish it for others to use.
How do I add a dependency on another package?
Add an entry to the manifest's dependencies array with .package(url:from:) (or another version rule), pointing at the other package's Git URL. Then, in each target that needs it, list the dependency under that target's dependencies using .product(name:package:). Only after both steps can your code import the dependency's module.
How do version rules like from work?
Version rules pin which versions of a dependency are acceptable, following semantic versioning. from: "1.2.0" is shorthand for up to the next major version, allowing 1.2.0 and later 1.x updates but not 2.0.0. You can also use .upToNextMajor, .upToNextMinor, or an exact version. This lets you receive safe updates while avoiding breaking changes.
How do I add a package in Xcode?
In Xcode choose File then Add Package Dependencies, paste the package's Git repository URL, pick a version rule, and select which products to add to which targets. Xcode resolves and downloads the package, and from then on you can import it like any framework. Behind the scenes this records the dependency just as the manifest would.