Swift Package Distribution
Introduction
After creating a Swift package, the next logical step is to distribute it so others can use your code in their projects. Swift package distribution involves preparing your package for public consumption, versioning it properly, and making it available through various distribution channels. This guide will walk you through the process of distributing your Swift packages effectively.
Understanding Package Distribution
Swift packages can be distributed in several ways, with the most common being:
- Version Control Systems (like GitHub, GitLab, BitBucket)
- Private Package Repositories
- Swift Package Registry
Before distributing your package, it's important to understand how Swift Package Manager (SPM) handles package resolution and versioning.
Versioning Your Swift Package
Swift Package Manager relies heavily on semantic versioning (SemVer), which follows the format MAJOR.MINOR.PATCH
:
- MAJOR: Incompatible API changes
- MINOR: Added functionality (backward-compatible)
- PATCH: Bug fixes (backward-compatible)
Setting Up Versioning
Versioning in Swift packages is handled using Git tags. When you're ready to release a version:
git tag 1.0.0
git push origin 1.0.0
This creates a tag that SPM can use as a version reference.
Version Rules in Package.swift
Your Package.swift
file includes version requirements for dependencies:
// Package.swift
let package = Package(
name: "MyPackage",
products: [
.library(name: "MyLibrary", targets: ["MyLibrary"]),
],
dependencies: [
// Examples of version requirements:
.package(url: "https://github.com/example/package1.git", from: "1.0.0"),
.package(url: "https://github.com/example/package2.git", .upToNextMajor(from: "2.0.0")),
.package(url: "https://github.com/example/package3.git", .exact("3.1.2")),
],
targets: [
.target(name: "MyLibrary", dependencies: ["package1", "package2", "package3"]),
]
)
Preparing Your Package for Distribution
1. Documentation
Good documentation is essential for package adoption. Consider including:
- A comprehensive README.md file
- API documentation using markup comments
- Usage examples
Here's an example of a documentation comment for a Swift function:
/// Processes an array of strings and returns consolidated results
///
/// This function takes multiple strings, processes them according to the specified options,
/// and returns a consolidated output.
///
/// - Parameters:
/// - inputs: An array of strings to process
/// - options: Options to control processing behavior
/// - Returns: A processed string result
/// - Throws: `ProcessingError` if the inputs cannot be processed
public func processStrings(_ inputs: [String], options: ProcessingOptions) throws -> String {
// Implementation
}
2. Testing
Ensure your package has comprehensive tests before distribution:
import XCTest
@testable import MyLibrary
final class MyLibraryTests: XCTestCase {
func testBasicFunctionality() {
// Given
let inputValue = "test"
// When
let result = MyLibrary.process(inputValue)
// Then
XCTAssertEqual(result, "PROCESSED: test")
}
// More tests...
}
3. License
Add a license file to your package to clarify how others can use your code:
touch LICENSE
Common open-source licenses include MIT, Apache 2.0, and GPL.
Publishing to GitHub
GitHub is the most common platform for distributing Swift packages:
- Create a repository on GitHub
- Push your local repository to GitHub:
git remote add origin https://github.com/yourusername/your-package.git
git push -u origin main
- Create and push a tag for your first release:
git tag 1.0.0
git push origin 1.0.0
Using Your Published Package
Once published, others can use your package by adding it to their Package.swift
file:
dependencies: [
.package(url: "https://github.com/yourusername/your-package.git", from: "1.0.0")
]
Or in Xcode:
- Select File > Swift Packages > Add Package Dependency
- Enter your repository URL
- Choose version requirements
- Select which targets should use the package
Real-World Example: Creating and Publishing a Utility Package
Let's walk through creating and publishing a simple string utility package:
1. Create the package
mkdir StringUtils
cd StringUtils
swift package init --type library
2. Implement functionality
// Sources/StringUtils/StringUtils.swift
public struct StringUtils {
public static func capitalize(_ string: String) -> String {
guard !string.isEmpty else { return string }
return string.prefix(1).uppercased() + string.dropFirst()
}
public static func reverse(_ string: String) -> String {
return String(string.reversed())
}
public static func isPalindrome(_ string: String) -> Bool {
let processed = string.lowercased().filter { $0.isLetter }
return processed == String(processed.reversed())
}
public init() {}
}
3. Write tests
// Tests/StringUtilsTests/StringUtilsTests.swift
import XCTest
@testable import StringUtils
final class StringUtilsTests: XCTestCase {
func testCapitalize() {
XCTAssertEqual(StringUtils.capitalize("hello"), "Hello")
XCTAssertEqual(StringUtils.capitalize(""), "")
XCTAssertEqual(StringUtils.capitalize("a"), "A")
}
func testReverse() {
XCTAssertEqual(StringUtils.reverse("hello"), "olleh")
XCTAssertEqual(StringUtils.reverse(""), "")
}
func testIsPalindrome() {
XCTAssertTrue(StringUtils.isPalindrome("racecar"))
XCTAssertTrue(StringUtils.isPalindrome("A man, a plan, a canal: Panama"))
XCTAssertFalse(StringUtils.isPalindrome("hello"))
}
}
4. Create a README.md
# StringUtils
A Swift package providing useful string manipulation utilities.
## Installation
Add the following dependency to your Package.swift:
```swift
dependencies: [
.package(url: "https://github.com/yourusername/StringUtils.git", from: "1.0.0")
]
Usage
import StringUtils
// Capitalize a string
let capitalized = StringUtils.capitalize("hello") // "Hello"
// Reverse a string
let reversed = StringUtils.reverse("hello") // "olleh"
// Check if a string is a palindrome
let isPalindrome = StringUtils.isPalindrome("racecar") // true
License
This package is released under the MIT license.
### 5. Publish to GitHub
```bash
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourusername/StringUtils.git
git push -u origin main
git tag 1.0.0
git push origin 1.0.0
Best Practices for Package Maintenance
- Keep a Changelog: Document changes between versions
- Semantic Versioning: Follow SemVer strictly to avoid breaking clients
- CI/CD Integration: Set up automated testing for every commit
- Swift Version Compatibility: Specify Swift version requirements
- Platform Support: Be clear about which platforms your package supports
Example Package.swift with Platform Requirements
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [
.iOS(.v13),
.macOS(.v11),
.watchOS(.v6),
.tvOS(.v13)
],
products: [
.library(name: "MyLibrary", targets: ["MyLibrary"]),
],
dependencies: [
// Dependencies go here
],
targets: [
.target(name: "MyLibrary", dependencies: []),
.testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
]
)
Private Package Hosting
For proprietary code, you can use:
- Private GitHub/GitLab repositories: Requires authentication
- Self-hosted Git servers: For complete control
- Swift Package Collections: For organizing and sharing packages within teams
Summary
Swift package distribution is an essential skill for sharing your code with the Swift community or within your organization. By following semantic versioning, providing good documentation, writing comprehensive tests, and using the appropriate platforms for distribution, you can make your Swift packages accessible and useful to other developers.
Remember these key points:
- Use Git tags for versioning
- Write clear documentation
- Include tests for your package
- Choose an appropriate license
- Specify platform and Swift version requirements
- Maintain your package with semantic versioning principles
Additional Resources
- Swift Package Manager Documentation
- Creating Swift Packages in Xcode
- Semantic Versioning Specification
Exercises
- Create a simple utility package and publish it to GitHub
- Add versioning to an existing Swift package
- Practice writing documentation comments for your package's public API
- Create a Swift Package Collection to group related packages
- Set up GitHub Actions to automatically run tests when changes are pushed to your package repository
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)