Skip to content
AgileLogics
6 min read

Swift Package Manager

Prerequisites: Access control

The Swift Package Manager (SwiftPM) is Swift's built-in tool for organizing code into packages, building it, testing it, and using other people's libraries. It ships with Swift and is fully integrated into Xcode.

Create a package

mkdir Tally
cd Tally
swift package init --type executable
Tally/
├── Package.swift          # the package manifest
└── Sources/
    └── main.swift         # your code
swift run
Building for debugging...
Build of product 'Tally' complete!
Hello, world!

swift build compiles without running; swift build -c release makes an optimized build.

The manifest: Package.swift

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "Tally",
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
    ],
    targets: [
        .executableTarget(
            name: "Tally",
            dependencies: [
                "TallyCore",
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
            ]
        ),
        .target(name: "TallyCore"),
        .testTarget(name: "TallyCoreTests", dependencies: ["TallyCore"]),
    ]
)
  • A target is one module. Once a package has several targets, each needs its own folder — so move main.swift into Sources/Tally/, and add Sources/TallyCore/ and Tests/TallyCoreTests/.
  • Putting logic in a library target (TallyCore) and keeping the executable thin makes the logic easy to test. Types that the executable uses from TallyCore must be public.
  • from: "1.5.0" accepts any version from 1.5.0 up to (not including) 2.0.0 — new features and fixes, but no breaking changes.

SwiftPM records the exact resolved versions in Package.resolved. Commit it for apps, so everyone builds with the same dependency versions.

In Xcode

For an app project, add packages with File → Add Package Dependencies... and paste the repository URL. The Swift Package Index (swiftpackageindex.com) is the place to search for packages.

Common mistakes

  • Forgetting public on types in a library target, then getting cannot find 'X' in scope in the target that imports it.
  • Depending on a package's main branch instead of a version. Branches change under you; versions don't.