The iOS Developer's Complete SPM Handbook: From Package Creation to Swift Package Index Publishing

Editorial team
Dot
May 26, 2026
Illustration of an iOS developer’s Swift Package Manager handbook showing Package.swift code, package creation, dependency management, testing, documentation, publishing, and Swift Package Index distribution workflow.

Swift Package Manager in new Project

Creating a New SPM Package from Scratch

When starting a new iOS or macOS project with Swift Package Manager from the beginning, you have two primary approaches: using Xcode or using the command line.

Method 1: Creating via Command Line (Official Approach)

Create a new library package
$ mkdir MyLibrary
$ cd MyLibrary
$ swift package init --type library

This command generates the following structure:

MyLibrary/
├── Package.swift (Manifest file - defines package configuration)
├── Sources/
│ └── MyLibrary/
│ └── MyLibrary.swift (Main source file)
├── Tests/
│ └── MyLibraryTests/
│ └── MyLibraryTests.swift
└── .gitignore

For executable packages, use: $ swift package init --type executable

Default Package.swift Structure
// swift-tools-version:5.9
import PackageDescription

let package = Package(
 name: "MyLibrary",
 platforms: [
 .iOS(.v13),
 .macOS(.v10_15)
 ],
 products: [
 .library(name: "MyLibrary", targets: ["MyLibrary"])
 ],
 dependencies: [
 // Add dependencies here
 ],
 targets: [
 .target(
 name: "MyLibrary",
 dependencies: []
 ),
 .testTarget(
 name: "MyLibraryTests",
 dependencies: ["MyLibrary"]
 )
 ]
)

Method 2: Creating via Xcode

1. Open Xcode

2. Go to File → New → Project

3. Select "Multiplatform" → "Package" template

4. Name your package and choose location

5. Xcode automatically generates the package structure with default Package.swift

Adding Dependencies in a New Project

When creating a new project, declare all dependencies in the Package.swift file before starting application development:

Example: Adding Multiple Dependencies

 let package = Package(
 name: "MyApp",
 platforms: [.iOS(.v15), .macOS(.v12)],
 products: [
 .library(name: "MyApp", targets: ["MyApp"])
 ],
 dependencies: [
 .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"),
 .package(url: "https://github.com/onevcat/Kingfisher.git", from: "7.0.0"),
 .package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.6.0")
 ],
 targets: [
 .target(
 name: "MyApp",
 dependencies: [
 "Alamofire",
 "Kingfisher",
 "SnapKit"
 ]
 ),
 .testTarget(
 name: "MyAppTests",
 dependencies: ["MyApp"]
 )
 ]
)

Version Constraint Options (Official)

Constraint Type Syntax Description
From Version .package(url: "...", from: "5.0.0") Updates allowed until next major version (RECOMMENDED)
Exact Version .package(url: "...", exact: "5.9.0") Pins to specific version only
Version Range .package(url: "...", "5.0.0"..<"6.0.0") Allows versions within specified range
Branch .package(url: "...", branch: "main") Tracks specific Git branch (NOT recommended for production)
Revision/Commit .package(url: "...", revision: "abc1234def") Pins to specific commit hash
Local Path .package(path: "../LocalPackage") References local package directory

Building and Testing New Packages

Command-line operations for new packages:

Command Purpose
$ swift build Compiles the package and all dependencies
$ swift test Runs all unit tests defined in the package
$ swift package show-dependencies Displays dependency tree/graph
$ swift package dump-package Outputs Package.swift manifest as JSON
$ swift run Executes an executable product from the package

Swift Package Manager in Exiting Projects

Adding SPM Dependencies to Existing Xcode Projects

For existing iOS or macOS projects that currently use CocoaPods, Carthage, or no dependency manager, adding SPM is straightforward through Xcode (version 11.0 or later).

Step-by-Step: Adding First Package via Xcode

1. Open your existing Xcode project (.xcodeproj file)

2. Select the project in the Navigator (blue project icon)

3. Click on the project name (NOT the target) to open Project Settings

4. Navigate to the "Package Dependencies" tab

5. Click the "+" button in the bottom left corner

6. Enter the package repository URL (e.g.,https://github.com/RevenueCat/purchases-ios.git)

7. Select the version constraint rule (recommended: "Up to Next Major Version")

8. Click "Add Package"

9. Xcode automatically resolves and fetches the package

10. Select which products to add to which targets

11. Click "Add Package" again to finalize

Linking Packages to Targets

After Xcode adds the package, it automatically appears in the build settings. You must then link the package product to your app target:

  • In your project target → Build Phases → Link Binary With Libraries → Ensure the package framework is listed and enabled
  • In your Swift code, import the package: import PackageName

Example: Importing and Using Alamofire

import Alamofire

func fetchUserData() {
 AF.request("https://api.example.com/user").responseJSON { response in
 switch response.result {
 case .success(let data):
 print("Data: \(data)")
 case .failure(let error):
 print("Error: \(error)")
 }
 }
}

Managing Package.resolved File

When you add a package via Xcode, a Package.resolved file is automatically generated and placed in: 

xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

  • CRITICAL: Always commit Package.resolved to version control.
  • This ensures all team members and CI/CD pipelines use identical dependency versions.
git add .xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Migrating from CocoaPods to SPM

Identify Equivalent SPM Packages

  • Search for SPM equivalents at swiftpackageindex.com or GitHub. Most popular pods (Alamofire, Kingfisher, SwiftyJSON, etc.) have SPM support.

Add SPM Packages Incrementally

  • Do NOT remove all CocoaPods at once. Add one or two SPM packages and test thoroughly.

Run Integration Tests

  • After each package addition: Build the project, run unit tests, test the feature using that package.

Clean Up CocoaPods

  • Once ALL dependencies are migrated to SPM, execute:

$ pod deintegrate

Then delete:

$ pod deintegrate

Podfile.lock

Pods/ directory

Commit and Update CI/CD

$ git add .
$ git commit -m "Migrate from CocoaPods to SPM"
$ git push

Update CI/CD pipelines: Remove pod install steps, xcodebuild will handle SPM automatically.

Creating and validating SPM Packages

Official Package.swift Requirements

According to official Swift.org documentation, every Package.swift must include:

  • Tools Version Declaration (MANDATORY)
// swift-tools-version:5.9

This MUST be the first line. It specifies the minimum Swift version required to build. Valid versions:

5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9

  • Package Name
  • Must be unique if published to package registries
  • Used for import statements and dependency references
  • Platforms Declaration
  • Specify minimum deployment targets:
platforms: [
 .iOS(.v13),
 .macOS(.v10_15),
 .watchOS(.v6),
 .tvOS(.v13)
]

  • Products Definition
  • Specify what the package exports (libraries, executables):
products: [
 .library(name: "MyPackage", targets: ["MyPackage"])
]

  • Targets Definition
  • Include at least one target with source files:
targets: [
 .target(name: "MyPackage", dependencies: []),
 .testTarget(name: "MyPackageTests", dependencies: ["MyPackage"])
]

Complete Valid Package.swift Example

// swift-tools-version:5.9
import PackageDescription

let package = Package(
 name: "NetworkingKit",
 platforms: [
 .iOS(.v13),
 .macOS(.v10_15),
 .watchOS(.v6),
 .tvOS(.v13)
 ],
 products: [
 .library(
 name: "NetworkingKit",
 targets: ["NetworkingKit"]
 )
 ],
 dependencies: [
 .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0")
 ],
 targets: [
 .target(
 name: "NetworkingKit",
 dependencies: ["Alamofire"]
 ),
 .testTarget(
 name: "NetworkingKitTests",
 dependencies: ["NetworkingKit"]
 )
 ]
)

Official Validation Commands

Before publishing, validate your package using these official commands:

Command 1: Validate Package Structure

$ cd MyPackage
$ swift package validate

Checks:

  • Package.swift syntax is valid
  • Required files exist (Sources/, Tests/)
  • Target names are properly configured

Command 2: Dump Package Manifest (CRITICAL for SPI)

$ swift package dump-package

  • Outputs your Package.swift as JSON. This command MUST succeed with valid JSON output. Swift Package Index (SPI) requires this.
  • Example output:
{
 "name" : "NetworkingKit",
 "products" : [
 {
 "name" : "NetworkingKit",
 "targets" : ["NetworkingKit"],
 "type" : {"library" : []}
 }
 ],
 "targets" : [
 {
 "name" : "NetworkingKit",
 "type" : "regular"
 }
 ]
}

Command 3: Build and Test

$ swift build # Compiles package
$ swift test # Runs tests

Both commands MUST complete without errors. Test coverage is not mandatory but recommended.

Command 4: Show Dependencies

$ swift package show-dependencies

Displays dependency tree. Useful for auditing what your package depends on.

Package Structure Best Practices

Directory Organization:

MyPackage/
├── .gitignore
├── .github/ (Optional: GitHub Actions, templates)
├── Package.swift (REQUIRED: Package manifest)
├── Sources/
│ └── MyPackage/
│ ├── MyPackage.swift (Main public API)
│ ├── Internal.swift (Internal implementation)
│ └── ...
├── Tests/
│ └── MyPackageTests/
│ ├── MyPackageTests.swift
│ └── ...
├── README.md (RECOMMENDED: Package description)
├── LICENSE (REQUIRED: Open source license)
└── CHANGELOG.md (OPTIONAL: Version history)

File Requirements for Package Publishing

File/Requirement Mandatory? Description
Package.swift YES Package manifest with valid syntax
Sources/PackageName/ YES At least one source file in target directory
Tests/ NO Test files (recommended but not mandatory)
README.md RECOMMENDED Description, usage examples, requirements
LICENSE YES Open source license file (MIT, Apache 2.0, etc.)
.gitignore RECOMMENDED Git ignore patterns for build artifacts
CHANGELOG.md OPTIONAL Version history and release notes
Documentation OPTIONAL DocC documentation (markdown files in Sources/)

Publishing to Swift Package Index

Swift Package Index (SPI) Overview

Swift Package Index (SPI) is the official community registry for Swift packages, backed by Apple. It provides discovery, documentation hosting, and package listing. SPI is NOT a required registry; packages work with SPM by Git URL alone. However, publishing to SPI increases discoverability.

Mandatory Requirements for SPI Publishing

According to official SPI documentation, packages must meet these requirements:

Public Git Repository

  • Requirement: Repository must be publicly accessible
  • Test: Visit the repository URL in a browser; should load without authentication
  • Example valid URLs:
https://github.com/user/package.git, https://gitlab.com/user/package.git

Valid Package.swift File

  • Requirement: Root directory must contain Package.swift
  • Must include swift-tools-version (first line)
  • Must define name, products, and targets
  • Test using:
$ swift package dump-package (must output valid JSON)

Swift Version Requirement

  • Requirement: Package must be written in Swift 5.0 or later
  • Tools version should be 5.5 or later for best compatibility: // swift-tools-version:5.5

Semantic Version Tags

  • Requirement: At least one Git release tag following semantic versioning
  • Examples of valid tags:
  • v1.0.0, 1.0.0, v1.0.1, 2.5.0
  • Create a tag using:
$ git tag v1.0.0
$ git push origin v1.0.0

Valid JSON from Package Dump

  • Requirement: swift package dump-package must output valid, parseable JSON
  • Test:
$ swift package dump-package | jq . (should not throw parse error)

Compiles Without Errors

  • Requirement: Package must build successfully
  • Test:
$ swift build (with latest Swift toolchain, must complete without compiler errors)

Code of Conduct Compliance

  • Requirement: Package content and repository must comply with SPI Code of Conduct
  • Includes: No discriminatory language, no malware, no illegal content

Recommended Requirements (Not Mandatory but Encouraged)

  • README.md with:
    • Package description and purpose
    • Installation instructions (SPM syntax)
  • Usage examples
  • Supported Swift and platform versions
  • LICENSE file with open source license text
  • MIT, Apache 2.0, GPL, BSD all acceptable
  • Comprehensive Tests:
    • Tests should be in Tests/PackageNameTests/ directory
  • Run tests:
$ swift test

  • Documentation:
    • DocC documentation for public APIs (Swift 5.5+)
    • In-code comments explaining complex logic

Adding Package to Swift Package Index

Method 1: Via Web UI (Recommended)

Step 1: Access the Add Package Page
  • Navigate to the official Swift Package Index website and go to the Add a Package section.
Step 2: Review Submission Requirements
  • Before proceeding, ensure you read and understand all the requirements for publishing a Swift Package on the platform.
Step 3: Initiate Package Addition
  • Click on the “Add Package(s)” button to start the process of submitting your Swift Package. Ref :


Step 4: Redirect to GitHub
  • After clicking the button, you will be redirected to the Swift Package Index GitHub repository where an issue template is used to generate your package submission request.
  • Here you will see the following options:
  1. Add a New Package
  2. Remove an Existing Package
  3. Report a Security Issue for a Package
  4. Create a Bug Report or Feature Request for an Existing Package

  • Step 5: Select Option 1
    • Choose Option 1: Add a New Package to proceed with deploying your Swift Package.
  • Step 6: Set the Issue Title
    • Provide a suitable title for the new package deployment in the issue form.
    Step 7: Add GitHub Repository URL
    • In the "New Packages" section of the form, enter the GitHub URL of the package you wish to submit.
  • Step 8: Submit the Request
    • Click the “Create” button to submit the request for your Swift Package to be added to the Swift Package Index.
  • Method 2: Via GitHub (Pull Request to PackageList)Alternative: Submit a PR to the PackageList repository
  • Post-Publishing: What SPI Does
    • After publishing to SPI, the service automatically:
    • Indexes your package for discovery
    • Generates and hosts DocC documentation (if available)
    • Monitors new releases and updates package metadata
    • Provides shields.io badges showing Swift version compatibility
    • Tracks package statistics (downloads, popularity)
    Example: Complete Publishing Workflow

Create package locally

$ swift package init --type library --name MyPackage
$ cd MyPackage

Develop and test

$ swift build
$ swift test


Validate before publishing

$ git init
$ git add .
$ git commit -m "Initial commit"
$ git branch -M main
$ git remote add origin https://github.com/youruser/MyPackage.git
$ git push -u origin main


Create GitHub repository

$ git init
$ git add .
$ git commit -m "Initial commit"
$ git branch -M main
$ git remote add origin https://github.com/youruser/MyPackage.git
$ git push -u origin main


Create semantic version tag

$ git tag v1.0.0
$ git push origin v1.0.0


Add to Swift Package Index

# Visit: https://swiftpackageindex.com/add-a-package
# Enter: https://github.com/youruser/MyPackage.git
# Click "Add Package"


Monitor SPI for indexing status

# Visit: https://swiftpackageindex.com/youruser/MyPackage

Official Commands and validation References 

Complete Command Reference

Command Description
swift package init --type library Create new library package
swift package init --type executable Create new executable package
swift build Build package (debug mode by default)
swift build -c release Build package in release mode
swift test Run all unit tests
swift test --filter TestName Run specific test
swift package resolve Resolve and fetch all dependencies
swift package update Update dependencies to latest allowed versions
swift package show-dependencies Display dependency tree
swift package dump-package Output Package.swift as JSON (CRITICAL)
swift package validate Validate package structure
swift run Run executable product
swift package generate-xcodeproj Generate Xcode project (legacy, rarely needed)

Validation Checklist Before Publishing

  •  First line: // swift-tools-version:X.X (where X.X >= 5.0)
  •  Package name is unique and follows naming conventions (lowercase, hyphen-separated)
  •  All required fields present: name, products, targets
  •  All targets have corresponding directories in Sources/ or Tests/
  •  swift package validate completes without errors
  •  swift build completes without errors
  •  swift test completes without errors
  •  swift package dump-package outputs valid JSON
  •  All external dependencies are publicly accessible
  •  All external dependencies have semantic version tags
  •  README.md exists with package description and usage
  •  LICENSE file exists (MIT, Apache 2.0, GPL, or other OSS license)
  •  .gitignore includes: .build/, .swiftpm/, .DS_Store
  •  Git repository is public (not private)
  •  At least one semantic version tag created (e.g., v1.0.0)
  •  CHANGELOG.md created (optional but recommended)

Troubleshooting Common Validation Issues

Problem: swift package dump-package outputs JSON errors

Solution: Check Package.swift syntax (missing commas, incorrect brackets). Run swift build to see specific syntax errors.

Problem: swift build fails with compiler errors

Solution: Review error messages. Ensure all imported dependencies are declared in Package.swift. Test with latest Swift version.

Problem: SPI shows "Repository not found" or "Invalid URL"

Solution: Ensure URL includes .git extension and is publicly accessible. Test URL in browser.

Problem: SPI shows "No valid versions found"

Solution: Create a semantic version Git tag (e.g., git tag v1.0.0 && git push origin v1.0.0).

Problem: Dependency resolution fails

Solution: Run swift package resolve. Check that dependency URLs are correct and public. Verify version constraints are compatible.

Problem: Tests fail but package builds

Solution: Fix test issues. Tests can fail, but they should run without crashing. Check test target dependencies in Package.swift.

Version Constraint Reference

Quick reference for all version constraint types:

Constraint Syntax Example Behavior
from (Recommended) from: "1.0.0" Allows ≥1.0.0 and <2.0.0
exact exact: "1.5.0" Only allows exactly 1.5.0
closed range "1.0.0"..."2.0.0" Allows 1.0.0 through 2.0.0 (inclusive)
half-open range "1.0.0"..<"2.0.0" Allows 1.0.0 through <2.0.0 (exclusive upper)
branch branch: "main" Tracks main branch (not for production)
revision revision: "abc123" Pins to specific commit hash
local path path: "../Package" Local package reference (development only)

Summary and Quick Start Guide

For New Projects:

swift package init --type library --name MyPackage
cd MyPackage
swift build && swift test

For Existing Projects:

# In Xcode:
# 1. File → Add Package Dependencies
# 2. Enter package URL
# 3. Choose version constraint
# 4. Select target to link
# 5. Commit Package.resolved to git

Conclusion

Swift Package Manager has matured into the go-to dependency solution for iOS and macOS development — and this guide covers everything you need to use it confidently, from initializing a package to publishing on the Swift Package Index.

Whether you're starting fresh, migrating from CocoaPods, or sharing your own library with the community, the workflow is consistent: keep your Package.swift clean, use semantic versioning, validate before publishing, and always commit Package.resolved. Follow these fundamentals and SPM will rarely give you trouble.

Build it, validate it, publish it — and let the Swift community find it.

Official Resources (From Verified Sources):

FAQ’s

No items found.

Actionable Insights,
Straight to Your Inbox

Subscribe to our newsletter to get useful tutorials , webinars,use cases, and step-by-step guides from industry experts

Start Pushing Real-Time App Updates Today
Try AppsOnAir for Free
Stay Uptodate