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
#endifCommand-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