Swift Package Manager

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Swift Package Manager (SwiftPM) is Swift’s built-in build system and dependency manager — a Package.swift manifest, written in Swift itself, replaces the project files a separate IDE would otherwise own, and the same manifest drives swift build, Xcode, and CI identically on every platform Swift supports.

Package.swift and PackageDescription

// swift-tools-version: 6.1
import PackageDescription

let package = Package(
    name: "WeatherKit",
    platforms: [.macOS(.v14), .iOS(.v17)],           // omit entirely for a platform-agnostic package
    products: [
        .library(name: "WeatherKit", targets: ["WeatherKit"]),
        .executable(name: "weather-cli", targets: ["WeatherCLI"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
        .package(path: "../SharedModels")                       // a sibling checkout, not a remote fetch
    ],
    targets: [
        .target(
            name: "WeatherKit",
            dependencies: ["SharedModels"],
            resources: [.process("Resources")],
            swiftSettings: [.swiftLanguageMode(.v6)]
        ),
        .executableTarget(
            name: "WeatherCLI",
            dependencies: [
                "WeatherKit",
                .product(name: "ArgumentParser", package: "swift-argument-parser")
            ]
        ),
        .testTarget(name: "WeatherKitTests", dependencies: ["WeatherKit"])
    ]
)

The leading // swift-tools-version: comment (see Getting Started) selects which version of the PackageDescription API the manifest itself is parsed against — a manifest that uses a Swift 6.1-only API (such as swiftSettings traits added in that release) needs at least that tools version, independent of which language mode the package’s targets compile in. Package(name:platforms:products:dependencies:targets:) is the manifest’s single top-level value; every other declaration is a static factory method on Target, Product or Package.Dependency.

Products, Targets and Test Targets

A product is what a package exposes to other packages or to a build system outside SwiftPM: .library (a set of targets consumers can import) or .executable (a runnable binary). A target is a buildable unit of source, one directory under Sources/<TargetName>/ (or Tests/<TargetName>/ for a test target) by convention:

Target kind Purpose

.target

Ordinary library code, importable by other targets in the same package.

.executableTarget

A main.swift/@main entry point, buildable and runnable with swift run.

.testTarget

Swift Testing or XCTest code, run with swift test — see Testing; never part of a product, so it never ships to consumers.

.macro

A macro-plugin target built as a separate compiler-plugin executable — see Macros.

.systemLibrary

Wraps a C library already installed on the system rather than one SwiftPM builds — see Interoperability with C, Objective-C and C++.

.plugin

A build-tool or command plugin, declared with a capability: (.buildTool() or .command(…​)) rather than compiled application code.

Dependencies, Version Rules and Conditions

dependencies: [
    .package(url: "https://github.com/apple/swift-collections.git", from: "1.1.0"),         // upToNextMajor
    .package(url: "https://github.com/apple/swift-algorithms.git", .upToNextMinor(from: "1.2.0")),
    .package(url: "https://github.com/apple/swift-syntax.git", exact: "600.0.1"),
    .package(url: "https://github.com/apple/swift-log.git", branch: "main"),
    .package(id: "apple.swift-numerics", from: "1.0.0"),     // resolved via a package registry, not a URL
    .package(path: "../SharedModels")                        // a local, unversioned checkout
]

from: resolves to the next major version below the following one (SwiftPM’s default, equivalent to .upToNextMajor(from:)); .upToNextMinor(from:) and exact: narrow that further; branch:/revision: pin a non-tagged commit, useful during development but not for a published package. A conditional dependency on a target scopes when it applies:

.target(
    name: "App",
    dependencies: [
        .product(name: "ArgumentParser", package: "swift-argument-parser"),
        .target(name: "LinuxOnlyHelpers", condition: .when(platforms: [.linux]))
    ]
)

.when(platforms:) and .when(configuration:) (.debug/.release) restrict a dependency, and equally a resources: entry (.process, which lets SwiftPM transform the file, or .copy, verbatim) or a linkerSetting/ swiftSetting, to only the platforms or build configurations where it applies.

Building, Running, Testing and Publishing

swift build                              # debug build of every target
swift build -c release                   # release build
swift run weather-cli --city Madrid      # build (if needed) and run an executable target
swift test                               # run every test target
swift test --filter WeatherKitTests/parsesForecast
swift package resolve                    # resolve dependencies without building
swift package update                     # re-resolve to the newest versions the rules allow
swift package generate-documentation     # DocC, via the swift-docc-plugin -- see
                                          # xref:programming-languages/swift/build-and-tooling.adoc[Build and Tooling]
swift package plugin --list              # discover build-tool/command plugins available to this package

Package.resolved is the lockfile SwiftPM writes after resolution: it pins every dependency (direct and transitive) to an exact version, and committing it is what makes a package’s build reproducible across machines and CI runs the same way a lockfile does in other ecosystems — swift package update is the explicit, deliberate way to move it forward. A local dependency (.package(path:)) is a sibling directory checked out on disk, with no version at all, used for developing two packages together; a registry dependency (.package(id:)) resolves against a Swift package registry (SE-0292) by package identifier rather than a Git URL, which is how a private or corporate registry can host packages without exposing their source location.

The package access level (see Access Control) exists specifically for this multi-target shape: a symbol marked package is visible to every target within the same package but invisible outside it, which is the boundary SwiftPM’s own target graph is built around.

swift-format integration comes from the swift-format build-tool plugin (declared as a dependency on https://github.com/apple/swift-format), which lets swift package plugin --list (or an IDE’s plugin menu) lint or reformat every target’s sources using the package’s own .swift-format configuration — see Build and Tooling for swift-format outside SwiftPM.

Publishing

Publishing a package is, at minimum, tagging a commit with a semantic-versioned Git tag (git tag 1.2.0) that consumers' from:/upToNextMajor rules resolve against — there is no separate build/upload step for a Git-hosted package. Publishing to a Swift package registry instead (SE-0391) additionally requires swift package-registry publish, which packages and signs a release and uploads it to the registry’s API rather than relying on Git hosting at all.

For the common public case — a package hosted on GitHub (or any Git host) and resolved by consumers via from:/upToNextMajor, as shown throughout this page — no registry account is needed at all: pushing a semver Git tag is sufficient, and that tag is immediately resolvable by anyone with the repository’s URL. A private or corporate Swift package registry is the exception: publishing to one does require an account on that registry and an auth token, supplied via swift package-registry login <url> --token <TOKEN> before swift package-registry publish can authenticate.

A GitHub Actions release workflow for the common Git-hosted case needs no secrets at all — it only has to validate the package and turn the pushed tag into a GitHub Release:

name: build

on:
  push:
    tags: [ 'v*' ]

jobs:
  build:
    runs-on: macos-latest

    steps:
      - uses: actions/checkout@v4

      - run: swift build
      - run: swift test

      - name: Draft a GitHub Release from the tag
        uses: softprops/action-gh-release@v2
        with:
          generate_release_notes: true

For an Objective-C library, see also Build and Tooling for publishing via CocoaPods.

flowchart TD P1["Product: .library(#quot;WeatherKit#quot;)"] --> T1["Target: WeatherKit"] P2["Product: .executable(#quot;weather-cli#quot;)"] --> T2["Target: WeatherKit CLI"] T2 --> T1 T1 --> D1["Dependency: SharedModels (local path)"] T2 --> D2["Dependency: swift-argument-parser (from: 1.5.0)"] T3["Target: WeatherKitTests (.testTarget)"] --> T1

A package’s products sit at the top of the graph and are the only thing a consumer sees; internal targets (and the test target, which is never part of a product) form the dependency graph beneath them that SwiftPM actually compiles.

See Also

References

TSPL: no dedicated chapter — SwiftPM is a tool documented separately from the language itself. Swift Package Manager documentation; PackageDescription API reference; SE-0292; SE-0391.