Skip to content
All essays
iOSMarch 27, 202511 min

Swift Package Manager: Dependency Management

Create packages, add dependencies, and build modular Swift projects with SPM.

Ü
Ümit Uz
Mobile & Full Stack Developer

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.swift

Package.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

  1. 1File → Add Package Dependencies
  2. 2Enter package URL
  3. 3Choose version
  4. 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-documentation

Versioning

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

  1. 1Semantic versioning: Use Major.Minor.Patch format
  2. 2Minimal dependencies: Avoid unnecessary dependencies
  3. 3Explicit versions: Specify versions for reliability
  4. 4Public API: Design public interfaces carefully
  5. 5Documentation: Use Swift documentation comments

Swift Package Manager is essential for modern Swift development!

Related essays

Next essay
iOS Architecture Patterns: MVC, MVVM, VIPER