Swift Package Manager (SPM) is Apple's official tool for dependency management and modular code organization in Swift projects.
Creating a Swift Package
bash
# Create a new package
swift package init --type library
# Or executable package
swift package init --type executable
# Package structure
├── Package.swift
├── Sources/
│ ├── MyPackage/
│ │ └── MyPackage.swift
│ └── MyPackageTests/
│ └── MyPackageTests.swiftPackage.swift Structure
swift
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyAwesomePackage",
platforms: [
.iOS(.v13),
.macOS(.v10_15)
],
products: [
.library(
name: "MyAwesomePackage",
targets: ["MyAwesomePackage"]
),
.executable(
name: "MyExecutable",
targets: ["MyExecutable"]
)
],
dependencies: [
// External dependencies
.package(
url: "https://github.com/Alamofire/Alamofire.git",
from: "5.8.0"
)
],
targets: [
.target(
name: "MyAwesomePackage",
dependencies: [
"Alamofire"
],
path: "Sources/MyAwesomePackage"
),
.testTarget(
name: "MyAwesomePackageTests",
dependencies: ["MyAwesomePackage"]
)
]
)Adding Dependencies
Local Package
swift
dependencies: [
.package(
path: "../SharedPackage"
)
]Remote Package
swift
dependencies: [
// Version range
.package(
url: "https://github.com/SwiftJSON/SwiftJSON.git",
from: "5.0.0"
),
// Branch
.package(
url: "https://github.com/apple/swift-log.git",
branch: "main"
),
// Exact version
.package(
url: "https://github.com/apple/swift-argument-parser.git",
exact: "1.0.0"
)
]Adding Dependencies with Xcode
- 1File → Add Package Dependencies
- 2Enter package URL
- 3Choose version
- 4Add Package
Modular Code Organization
Intra-package Modules
swift
// Sources/MyPackage/Networking/Networking.swift
public struct NetworkClient {
public func fetch() async throws -> Data {
// Implementation
}
}
// Sources/MyPackage/Analytics/Analytics.swift
public struct Analytics {
public func track(_ event: String) {
// Implementation
}
}Multiple Target Package
swift
let package = Package(
name: "ModularApp",
targets: [
.target(
name: "Core",
dependencies: []
),
.target(
name: "Networking",
dependencies: ["Core"]
),
.target(
name: "UI",
dependencies: ["Core", "Networking"]
),
.target(
name: "App",
dependencies: ["UI"]
)
]
)Swift Package with SwiftUI
swift
// Package.swift
let package = Package(
name: "SwiftUIComponents",
platforms: [.iOS(.v13)],
products: [
.library(
name: "SwiftUIComponents",
targets: ["SwiftUIComponents"]
)
],
targets: [
.target(
name: "SwiftUIComponents",
dependencies: []
)
]
)swift
// Sources/SwiftUIComponents/PrimaryButton.swift
import SwiftUI
public struct PrimaryButton: View {
let title: String
let action: () -> Void
public init(title: String, action: @escaping () -> Void) {
self.title = title
self.action = action
}
public var body: some View {
Button(action: action) {
Text(title)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(10)
}
}
}Plugin System
swift
// Package.swift
let package = Package(
name: "MyPackage",
targets: [
.target(
name: "MyPackage",
plugins: [
// SwiftLint plugin
.plugin(name: "SwiftLintPlugin", package: "SwiftLintPlugins")
]
)
],
dependencies: [
.package(
url: "https://github.com/example/SwiftLintPlugins.git",
from: "1.0.0"
)
]
)Adding Resources
swift
.target(
name: "MyPackage",
resources: [
.process("Resources"), // Processed resources
.copy("Assets.xcassets") // Direct copy
]
)Binary Framework
swift
let package = Package(
name: "MyApp",
targets: [
.binaryTarget(
name: "MySDK",
url: "https://example.com/MySDK.xcframework.zip",
checksum: "abc123..."
)
]
)Build and Test
bash
# Build package
swift build
# Release build
swift build -c release
# Run tests
swift test
# Test coverage
swift test --enable-code-coverage
# Run specific test
swift test --filter testAddition
# Generate documentation
swift package generate-documentationVersioning
swift
// Tagging
git tag 1.0.0
git push origin 1.0.0
// Version in Package.swift
let package = Package(
name: "MyPackage",
// Version is automatically taken from git tag
)
// Dependency versioning
.package(
url: "https://github.com/example/package.git",
// 1.0.0 <= version < 2.0.0
from: "1.0.0"
// or range: "1.0.0"..<"2.0.0"
)Best Practices
- 1Semantic versioning: Use Major.Minor.Patch format
- 2Minimal dependencies: Avoid unnecessary dependencies
- 3Explicit versions: Specify versions for reliability
- 4Public API: Design public interfaces carefully
- 5Documentation: Use Swift documentation comments
Swift Package Manager is essential for modern Swift development!