Creating Your Own Swift Package: An Arithmetic Example

Swift Package Manager is a tool that helps you manage and distribute Swift code. It lets you create reusable packages that can be used in your own projects or shared with other developers. It also automates the process of downloading, compiling, and linking dependencies.

In this blog post, I will show you how to create a simple Swift package that provides an Arithmetic class with generic methods for addition, subtraction, multiplication, and division. These methods can work with any type that conforms to the Numeric protocol. I will also show you how to handle errors that may occur during the arithmetic operations.

What is a Swift Package?

A Swift package is a collection of Swift source files and a manifest file that defines the package’s name and its contents. A package can have one or more targets, which specify the products (libraries or executables) that the package builds and the dependencies that they require. A dependency is another package that provides a module that can be imported by the code in the package.

How to Create a Swift Package?

You can create a Swift package using either Xcode or the command line. In this example, I will use the command line, but you can also follow the steps in Xcode if you prefer.

To create a new Swift package, open the Terminal application and create a folder named ArithmeticPackage:

$ mkdir ArithmeticPackage
$ cd ArithmeticPackage

Then, use the swift package init command to generate a basic directory structure and a manifest file:

$ swift package init --type=library

The --type=library option tells the Swift Package Manager to create a library product, which is a module that can be imported by other Swift code. You can also use --type=executable to create an executable product, which is a program that can be run by the operating system².

The swift package init command will create the following files and folders:

  • The README.md file resides at the root level of the package. It describes the functionality of your Swift package.
  • The Package.swift file, or package manifest, describes the configuration for the Swift package. You can double-click it in Finder to open the package in Xcode. The package manifest uses Swift and the PackageDescription framework to define the package’s name, products, targets, dependencies on other packages, and so on.
  • Source files reside in a folder named Sources and are scoped per Target. A Swift package can contain several targets, and, as a convention, each target’s code resides in its own subfolder.
  • Unit test targets reside in a folder named Tests, and, following the same convention as standard targets, each test target’s code resides in its own subfolder.

How to Add Code to Your Swift Package?

To add code to your Swift package, you need to edit the source files in the Sources folder. By default, the swift package init command will create a target named after your package name, and a source file with the same name. In this example, we have a target named ArithmeticPackage and a source file named ArithmeticPackage.swift.

To create our Arithmetic class, we need to edit the ArithmeticPackage.swift file and add the following code:

import Foundation

public enum ArithmeticError: Error {
    case divisionByZero
}

public class Arithmetic<T: Numeric & FloatingPoint> {
    public init() {}
    
    public func add(_ x: T, _ y: T) -> T {
        return x + y
    }
    
    public func sub(_ x: T, _ y: T) -> T {
        return x - y
    }
    
    public func mult(_ x: T, _ y: T) -> T {
        return x * y
    }
    
    public func div(_ x: T, _ y: T) throws -> T {
        guard y != 0 else {
            throw ArithmeticError.divisionByZero
        }
        return x / y
    }
}

The import Foundation statement imports the Foundation framework, which provides some basic types and functions for Swift. The public keyword makes our class and its methods accessible from outside the module. The init() method is an initializer that creates an instance of our class. The add(), sub(), mult(), and div() methods are generic arithmetic operations that take two parameters of type T and return a result of type T. The type T must conform to the Numeric protocol, which defines the basic arithmetic operators for numeric types. The div() method also throws an error of type ArithmeticError if the second parameter is zero, to prevent a division by zero error.

How to Build and Test Your Swift Package?

To build your Swift package, you can use the swift build command, which downloads and builds all the dependencies and produces binary files in a .build directory:

$ swift build

To test your Swift package, you can use the swift test command, which runs all the tests defined in your package:

$ swift test

By default, the swift package init command will create a test target named after your package name with Tests suffix, and a test file with the same name. In this example, we have a test target named ArithmeticPackageTests and a test file named ArithmeticPackageTests.swift.

To write tests for our Arithmetic class, we need to edit the ArithmeticPackageTests.swift file and add the following code:

import XCTest
@testable import ArithmeticPackage

final class ArithmeticPackageTests: XCTestCase {
    func testAdd() {
        let arithmetic = Arithmetic<Int>()
        XCTAssertEqual(arithmetic.add(1, 2), 3)
    }
    
    func testSub() {
        let arithmetic = Arithmetic<Int>()
        XCTAssertEqual(arithmetic.sub(3, 2), 1)
    }
    
    func testMult() {
        let arithmetic = Arithmetic<Int>()
        XCTAssertEqual(arithmetic.mult(2, 3), 6)
    }
    
    func testDiv() {
        let arithmetic = Arithmetic<Int>()
        XCTAssertEqual(try arithmetic.div(6, 3), 2)
    }
    
    func testDivByZero() {
        let arithmetic = Arithmetic<Int>()
        XCTAssertThrowsError(try arithmetic.div(6, 0))
    }
}

The @testable import ArithmeticPackage statement imports our module with internal access level so we can access our class from our tests. The XCTestCase class is an abstract base class for test cases that provides some methods for testing assertions. The testAdd(), testSub(), testMult(), and testDiv() methods are test cases that create an instance of our class and use the XCTAssertEqual() method to check if our methods return the expected results. The testDivByZero() method is a test case that checks if our div() method throws an error when dividing by zero.

How to Use Your Swift Package in Other Projects?

To use your Swift package in other projects, you need to publish it to a remote repository (such as on GitHub) and tag it with a version number using semantic versioning. You can then add it as a dependency to your project using Xcode or by editing your manifest file manually.

To publish your Swift package to GitHub, you need to create a repository with the same name as your package (in this example, ArithmeticPackage) and push your code there:

$ git init
$ git remote add origin https://github.com/your_username/ArithmeticPackage.git
$ git add .
$ git commit -m "Initial commit"
$ git push -u origin main

To tag your Swift package with a version number (in this example, 1.0.0), you need to use the git tag command and push it to GitHub:

$ git tag 1.0.0
$ git push origin 1.0.0

To add your Swift package as a dependency to your project using Xcode, you need to open your project and select File > Add Packages… Then enter your repository URL (in this example https://github.com/your_username/ArithmeticPackage.git) and choose your version requirement (in this example Up to Next Major). Xcode will then resolve and fetch your dependency and let you import it in your code.

To add your Swift package as a dependency to your project by editing your manifest file manually, you need to add a dependency object to the dependencies array in your Package.swift file. The dependency object takes the URL of the package repository and a version requirement. For example:

// swift-tools-version:5.3
import PackageDescription

let package = Package(
    name: "MyProject",
    dependencies: [
        .package(url: "https://github.com/your_username/ArithmeticPackage.git", .upToNextMajor(from: "1.0.0"))
    ],
    targets: [
        .target(
            name: "MyProject",
            dependencies: ["ArithmeticPackage"]),
        .testTarget(
            name: "MyProjectTests",
            dependencies: ["MyProject"]),
    ]
)
// Import the ArithmeticPackage module
import ArithmeticPackage

// Create an instance of the Arithmetic class with Double type
let arithmetic = Arithmetic<Double>()

// Use the methods to perform arithmetic operations
let sum = arithmetic.add(1.5, 2.5) // 4.0
let difference = arithmetic.sub(3.0, 1.0) // 2.0
let product = arithmetic.mult(2.0, 3.0) // 6.0

// Use the try-catch syntax to handle errors from the div() method
do {
    let quotient = try arithmetic.div(6.0, 2.0) // 3.0
    print(quotient)
} catch ArithmeticError.divisionByZero {
    print("Cannot divide by zero")
} catch {
    print("Unknown error")
}

This will add the ArithmeticPackage as a dependency to your MyProject target. You can then import it in your code and use its functionality.

Conclusion

These are the basic steps to create, build, test, publish, and use your own Swift package. Swift Package Manager is a powerful tool that helps you manage and distribute Swift code in a modular and reusable way. You can also leverage the code from other developers by adding their packages as dependencies to your projects.

Thanks for reading!