Swift Package Manager
02 / 02

Targets, Products, Resources & CLI

Swift Package Manager: Targets, Products, Resources & CLI

Target Types

targets: [
    // Library module — meant to be imported by other code
    .target(name: "MyLibrary", dependencies: []),

    // Produces a runnable CLI executable (main.swift or @main entry point)
    .executableTarget(name: "my-cli", dependencies: ["MyLibrary"]),

    // Test target — typically depends on the library it exercises
    .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),

    // Prebuilt .xcframework instead of source — closed-source SDKs,
    // or avoiding recompiling a large, stable dependency every build
    .binaryTarget(
        name: "SomeSDK",
        url: "https://example.com/SomeSDK.xcframework.zip",
        checksum: "a1b2c3..."
    ),
]

// A product bundles one or more targets behind what consumers actually see
products: [
    .library(name: "MyLibrary", targets: ["MyLibrary"]),
    .executable(name: "my-cli", targets: ["my-cli"]),
]

Resources & Platform Conditions

// Bundling non-code resources — accessed via the auto-generated Bundle.module
.target(
    name: "MyLibrary",
    resources: [
        .process("Resources/Icons.xcassets"), // platform-optimized (e.g. compiled)
        .copy("Resources/config.json"),        // copied verbatim, untouched
    ]
)

// In source:
let configURL = Bundle.module.url(forResource: "config", withExtension: "json")

// Platform-specific dependencies/targets
.target(
    name: "MyLibrary",
    dependencies: [
        .product(name: "UIKitHelper", package: "UIKitHelper",
                 condition: .when(platforms: [.iOS]))
    ]
)

// Inside source files, standard compiler conditionals still apply
#if canImport(UIKit)
import UIKit
#endif

Command-Line Workflow

# Scaffold a new package
swift package init --type library      # or --type executable

# Build all targets
swift build
swift build -c release

# Run the executable target
swift run my-cli --some-flag

# Run tests (XCTest or Swift Testing) — the CI equivalent of Cmd+U in Xcode
swift test
swift test --filter MyLibraryTests.testSomething

# Resolve/update dependencies without a full build
swift package resolve
swift package update

# Inspect the resolved dependency graph
swift package show-dependencies

# Build tool plugins can hook into this process — e.g. running a
# code generator before compilation — declared as .plugin targets
# with a .buildTool capability.

SPM vs CocoaPods vs Carthage

  • SPM is Apple's first-party tool, built into the Swift toolchain and Xcode — no external install, no committed workspace files.

  • A library only distributed via CocoaPods/Carthage needs its own Package.swift before an SPM-only project can consume it directly — there's no automatic bridge between registries.

  • SPM works outside Xcode too (swift build/test/run) — a key reason it fits server-side Swift and Linux cross-platform projects, not just iOS/macOS apps.

  • CocoaPods still leads on some resource-bundling edge cases (storyboards, complex asset pipelines) that push some mature codebases to stay on it.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free