Skip to main content

Swift Package Publishing

Introduction

Once you've built a useful Swift package, the next logical step is to share it with the world. Publishing your Swift package allows other developers to easily incorporate your code into their projects. This guide will walk you through the process of preparing your package for publication and the different ways you can make it available to the Swift developer community.

Swift packages can be published in several ways, with GitHub being the most common platform. More recently, Apple has introduced the Swift Package Registry, providing an official channel for package distribution. This tutorial covers both approaches and best practices to ensure your package is discoverable, well-documented, and follows community standards.

Prerequisites

Before publishing your Swift package, make sure you have:

  • A completed Swift package with meaningful functionality
  • Git installed on your system
  • A GitHub account (for GitHub publishing)
  • Swift 5.3 or later
  • Basic understanding of semantic versioning

Preparing Your Package for Publication

1. Package Structure and Organization

Ensure your package has a clear and organized structure:

MyPackage/
├── .gitignore
├── Package.swift
├── README.md
├── LICENSE
├── Sources/
│ └── MyPackage/
│ └── MyPackage.swift
└── Tests/
└── MyPackageTests/
└── MyPackageTests.swift

2. Creating Comprehensive Documentation

Good documentation is crucial for package adoption. At minimum, include:

README.md

Your README should contain:

markdown
# MyPackage

A brief description of what your package does.

## Installation

### Swift Package Manager

Add the following to your `Package.swift` file:

```swift
dependencies: [
.package(url: "https://github.com/username/MyPackage.git", from: "1.0.0")
]

Usage

swift
import MyPackage

let instance = MyPackage()
instance.doSomething()

Requirements

  • iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
  • Swift 5.3+

License

MyPackage is available under the MIT license.


### 3. Add a License

Including a license is essential for others to legally use your code:

touch LICENSE


Common licenses include MIT, Apache 2.0, or BSD. The MIT license is popular for its simplicity:

MIT License

Copyright (c) 2023 Your Name

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files...


### 4. Write Detailed API Documentation

Use Swift's documentation comments to explain your public API:

```swift
/// A utility class that provides string manipulation functionality.
///
/// Use this class when you need to perform common operations on strings
/// in a more convenient way.
///
/// ```swift
/// let helper = StringHelper()
/// let result = helper.capitalize("hello world")
/// // result is "Hello world"
/// ```
public struct StringHelper {
/// Creates a new StringHelper instance.
public init() {}

/// Capitalizes the first letter of the provided string.
///
/// - Parameter input: The string to capitalize
/// - Returns: A string with the first character capitalized
public func capitalize(_ input: String) -> String {
guard !input.isEmpty else { return input }
return input.prefix(1).uppercased() + input.dropFirst()
}
}

Versioning Your Package

Understanding Semantic Versioning

Swift packages use Semantic Versioning (SemVer), which follows the pattern MAJOR.MINOR.PATCH:

  • MAJOR: Breaking changes
  • MINOR: New features, backwards-compatible
  • PATCH: Bug fixes, backwards-compatible

Creating Tags for Releases

When you're ready to publish a version:

bash
git add .
git commit -m "Prepare for 1.0.0 release"
git tag 1.0.0
git push origin master --tags

Publishing to GitHub

GitHub is the most common hosting platform for Swift packages. Follow these steps to publish your package:

1. Create a GitHub Repository

  1. Go to GitHub and log in
  2. Click the "+" icon in the top-right and select "New repository"
  3. Name your repository (typically the same as your package name)
  4. Add a description
  5. Choose public visibility
  6. Don't initialize with README, .gitignore, or license (you already have these)
  7. Click "Create repository"

2. Push Your Code to GitHub

bash
git remote add origin https://github.com/username/MyPackage.git
git push -u origin master
git push origin --tags

3. Creating a GitHub Release

  1. Navigate to your repository
  2. Click "Releases" in the right sidebar
  3. Click "Draft a new release"
  4. Select your tag (e.g., "1.0.0")
  5. Add a title and description
  6. Click "Publish release"

Publishing to Swift Package Registry

Apple has introduced the Swift Package Registry, which is becoming the official platform for Swift package distribution.

1. Prepare Your Package for the Registry

Ensure your Package.swift has all required metadata:

swift
// swift-tools-version:5.5
import PackageDescription

let package = Package(
name: "MyPackage",
platforms: [
.macOS(.v10_15),
.iOS(.v13),
.tvOS(.v13),
.watchOS(.v6)
],
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"]),
],
dependencies: [
// Dependencies go here
],
targets: [
.target(
name: "MyPackage",
dependencies: []),
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]),
],
swiftLanguageVersions: [.v5]
)

2. Register with the Swift Package Registry

As of now, the Swift Package Registry requires registration. Follow these steps:

  1. Create an account on the Swift Package Registry website
  2. Register your package by providing the GitHub URL
  3. Verify ownership of the repository
  4. Submit your package for review

Best Practices for Package Publishing

1. Maintain Backward Compatibility

When updating your package, avoid breaking changes when possible:

swift
public struct UserProfile {
public let id: String
public let name: String

// Original initializer - keep this for backward compatibility
public init(id: String, name: String) {
self.id = id
self.name = name
}

// New initializer with additional parameters
public init(id: String, name: String, email: String? = nil) {
self.id = id
self.name = name
// Use the new parameter internally
}
}

2. Include Example Projects

Create an Examples directory with sample projects demonstrating your package:

MyPackage/
├── ...
└── Examples/
└── SimpleApp/
├── SimpleApp.xcodeproj
└── ...

3. Set Up Continuous Integration

Use GitHub Actions to automatically test your package:

Create a file at .github/workflows/tests.yml:

yaml
name: Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: swift build -v
- name: Run tests
run: swift test -v

4. Create Detailed Release Notes

For each new version, provide detailed release notes that explain:

  • New features
  • Bug fixes
  • Breaking changes and migration steps
  • Performance improvements

Real-World Example: Publishing a Networking Library

Let's walk through publishing a simple networking library called "SimpleNetworker":

1. Create the package structure

bash
mkdir SimpleNetworker
cd SimpleNetworker
swift package init --type library

2. Implement the functionality

In Sources/SimpleNetworker/SimpleNetworker.swift:

swift
import Foundation

public class SimpleNetworker {
public enum NetworkError: Error {
case invalidURL
case requestFailed(Error)
case invalidResponse
case decodingFailed(Error)
}

public init() {}

/// Performs a GET request and decodes the response to the specified type
/// - Parameters:
/// - urlString: The URL string for the request
/// - type: The decodable type to decode the response into
/// - completion: Completion handler with Result type
public func get<T: Decodable>(from urlString: String,
as type: T.Type,
completion: @escaping (Result<T, NetworkError>) -> Void) {
guard let url = URL(string: urlString) else {
completion(.failure(.invalidURL))
return
}

URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.requestFailed(error)))
return
}

guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode),
let data = data else {
completion(.failure(.invalidResponse))
return
}

do {
let decodedObject = try JSONDecoder().decode(type, from: data)
completion(.success(decodedObject))
} catch {
completion(.failure(.decodingFailed(error)))
}
}.resume()
}
}

3. Write tests

In Tests/SimpleNetworkerTests/SimpleNetworkerTests.swift:

swift
import XCTest
@testable import SimpleNetworker

final class SimpleNetworkerTests: XCTestCase {
func testSimpleNetworker() {
// Tests would go here
XCTAssertNotNil(SimpleNetworker())
}
}

4. Create README.md and LICENSE files

5. Initialize git and push to GitHub

bash
git init
git add .
git commit -m "Initial implementation"
git remote add origin https://github.com/username/SimpleNetworker.git
git push -u origin master

6. Tag for release

bash
git tag 1.0.0
git push origin --tags

7. Users can now add your package to their projects

swift
// In Package.swift
dependencies: [
.package(url: "https://github.com/username/SimpleNetworker.git", from: "1.0.0")
]

// Or in Xcode:
// File > Swift Packages > Add Package Dependency...
// Enter: https://github.com/username/SimpleNetworker.git

Updating Your Package

When you make changes to your package, follow these steps:

  1. Make your changes and update tests
  2. Update the version in your documentation
  3. Commit your changes
  4. Create a new tag following semantic versioning
  5. Push to GitHub
bash
git add .
git commit -m "Add async/await support"
git tag 1.1.0
git push origin master --tags

Summary

Publishing your Swift package makes your code available to the wider Swift development community. The key aspects of successful package publishing include:

  • Creating comprehensive documentation
  • Following semantic versioning
  • Structuring your package properly
  • Including a license
  • Publishing to GitHub and/or Swift Package Registry
  • Maintaining backward compatibility when possible
  • Testing thoroughly before each release

By following these guidelines, you'll create packages that are easy to discover, understand, and integrate into other developers' projects.

Additional Resources

Exercises

  1. Create a small utility Swift package and publish it to GitHub
  2. Add GitHub Actions for continuous integration to your package
  3. Create a new version of your package with additional features
  4. Write comprehensive documentation with examples for your package
  5. Update an existing package following semantic versioning rules


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)