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:
# 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
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:
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
- Go to GitHub and log in
- Click the "+" icon in the top-right and select "New repository"
- Name your repository (typically the same as your package name)
- Add a description
- Choose public visibility
- Don't initialize with README, .gitignore, or license (you already have these)
- Click "Create repository"
2. Push Your Code to GitHub
git remote add origin https://github.com/username/MyPackage.git
git push -u origin master
git push origin --tags
3. Creating a GitHub Release
- Navigate to your repository
- Click "Releases" in the right sidebar
- Click "Draft a new release"
- Select your tag (e.g., "1.0.0")
- Add a title and description
- 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-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:
- Create an account on the Swift Package Registry website
- Register your package by providing the GitHub URL
- Verify ownership of the repository
- Submit your package for review
Best Practices for Package Publishing
1. Maintain Backward Compatibility
When updating your package, avoid breaking changes when possible:
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
:
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
mkdir SimpleNetworker
cd SimpleNetworker
swift package init --type library
2. Implement the functionality
In Sources/SimpleNetworker/SimpleNetworker.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
:
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
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
git tag 1.0.0
git push origin --tags
7. Users can now add your package to their projects
// 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:
- Make your changes and update tests
- Update the version in your documentation
- Commit your changes
- Create a new tag following semantic versioning
- Push to GitHub
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
- Swift Package Manager Documentation
- GitHub's Guide to Open Source
- Choose a License - Help selecting the right license
- Semantic Versioning - Official SemVer documentation
- Swift API Design Guidelines
Exercises
- Create a small utility Swift package and publish it to GitHub
- Add GitHub Actions for continuous integration to your package
- Create a new version of your package with additional features
- Write comprehensive documentation with examples for your package
- 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! :)