How to Create and Publish Swift Packages

Photo by Heather Barnes on Unsplash
Photo by Heather Barnes on Unsplash
Swift packages are reusable code components. It can contain code, binaries, and resource files. We can easily use Swift packages in our app projects. This article describes how to build and publish Swift packages.

Swift packages are reusable code components. It can contain code, binaries, and resource files. We can easily use Swift packages in our app projects. This article describes how to build and publish Swift packages.

The complete code for this chapter can be found in .

Creating Swift Packages

There are two ways to create Swift packages. One is using Xcode and the other is using the command line.

Creating Swift Packages with Xcode

Xcode supports creating, publishing, and managing Swift packages. Creating Swift packages with Xcode is fairly simple. Open your Xcode, select File -> New -> Package.

Xcode -> New -> Package
Xcode -> New -> Package

Xcode will pop up the following dialog, asking you to enter a name for your Swift package and choose the path to store the Swift package. We enter Greeting as the name of our Swift package.

Name your Swift package.
Name your Swift package.

After clicking Create button, Xcode will generate a Swift package, as shown below. A standard Swift package contains a Package.swift, Sources folder, and Tests folder.

  • Package.swift: Configuration file for the Swift package. We will explain it in detail later.
  • Sources/: Under this folder, there is a folder named Greeting with the same name as this Swift package. All code should go under here.
  • Tests/: Under this folder, there is a folder whose name is the name of the Swift package plus Tests. All test code should go under here.
The project structure of a Swift package.
The project structure of a Swift package.

Creating Swift Packages with the Command Line

It’s also easy to create Swift packages using the command line. First create a folder whose name will be the name of this Swift package. Then, under this folder, enter swift package init --type library, which will generate a Swift package project.

% mkdir Greeting
% cd Greeting
Greeting % swift package init --type library
Creating library package: Greeting
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Greeting/Greeting.swift
Creating Tests/
Creating Tests/GreetingTests/
Creating Tests/GreetingTests/GreetingTests.swift

Package. swift

Package.swift is the configuration file for a Swift package. It mainly uses a Package object to define all configurations.

The first line of Package.swift is always the version number of the Swift tool that defines this Swift package to use. For version 5.4 and later, you may or may not have a leading blank. However, if you use 5.3 and earlier version, you cannot add blanks in front of it.

The Swift tool version defines:

  • Version of the PackageDescription library.
  • A minimum version of the Swift tools and Swift language compatibility version. They will then be used to process the manifest.
  • The required minimum version of Swift tools to use this Swift package.
// swift-tools-version: 5.4

The constructor of Package is as follows. It has many parameters, we will only explain a few commonly used parameters

Package.init(
    name: String,
    defaultLocalization: LanguageTag? = nil,
    platforms: [SupportedPlatform]? = nil,
    pkgConfig: String? = nil,
    providers: [SystemPackageProvider]? = nil,
    products: [Product] = [],
    dependencies: [Package.Dependency] = [],
    targets: [Target] = [],
    swiftLanguageVersions: [SwiftVersion]? = nil,
    cLanguageStandard: CLanguageStandard? = nil,
    cxxLanguageStandard: CXXLanguageStandard? = nil
)

The name defines the name of this Swift package.

The platforms lists the platforms supported by this Swift package. SupportedPlatform defines all available platforms.

The products lists the final products that this Swift package vends. When other Swift packages use this Swift package, they will see the list of products. Product defines three products:

  • .library(name:type:targets) : Create a library. Swift packages that integrate this library can use its public APIs.
    • name: The name of this library product.
    • type: Determine how the Swift packages that integrate this library should link this library. If this parameter is not specified, the Swift Package Manager can decide based on the preferences of the Swift packages integrating this library.
      • static : Define this as a statically linked library.
      • dynamic : Define this as a dynamically linked library.
    • targets: Define targets to vends as this product. These targets are defined in the targets parameter of Package.init().
  • .executable(name:targets:) : Create an executable.
  • .plugin(name:targets:) : Create a Swift package plugin.

The dependencies list the dependencies of this Swift package. Package.Dependency defines the source and version of a dependency.

  • package(path:) : From the given path, add a local package as a dependency.
  • package(url:from:) : From the given url, add a remote package as a dependency. Its version can be from the specified minimum version to the next major version.

The targets lists the targets that this Swift package will produce. Different from products, a product is bundled of one or several targets. Target defines a target.

  • .target (name:dependencies:) : Create a library target.
    • name: The name of this target.
    • dependencies: Specifies that this target depends on other targets. If this target depends on a dependencies defined in Package.init(), it must also be specified here.
  • .testTarget() : Create a test target.
  • .binaryTarget() : Create a binary target. This target refers to a binary artifact pointed to the given path.
  • .systemLibrary() : Create a system library target.
  • .executableTarget() : Create an executable target.
  • .plugin() : Create a package plugin target.

Below is the Package.swift of the Greeting package. We add a dependency called SwiftyJSON to dependencies. Then, in targets, we also specify that the target Greeting depends on SwiftyJSON.

// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Greeting",
    platforms: [
        .iOS(.v13)
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Greeting",
            targets: ["Greeting"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "Greeting",
            dependencies: ["SwiftyJSON"]),
        .testTarget(
            name: "GreetingTests",
            dependencies: ["Greeting"]),
    ]
)

Publishing Swift Packages

Publishing Swift packages for use by other Swift packages is easy. We only need to put our Swift package into a Git repository, then other Swift packages can integrate our Swift package into the project through the URL of this Git repository. As for the version, we can add a version through Git tag.

The above publishing process can be done using Xcode, the command line, or any Git client app.

Publishing Swift Packages with Xcode

Click Source Control -> New Git Repository.

Source Control -> New Git Repository...
Source Control -> New Git Repository…

Choose the current Swift package to create a Git repository.

Create Git repositories for projects.
Create Git repositories for projects.

At this time, the Git repository of the current Swift package has been created. Xcode also created the first commit for us.

On the Navigator, click Show the source control navigator, and select Repositories. Then right click on Remotes and select New “Greeting” Remote..

New Git remote.
New Git remote.

At this point Xcode will pop up the following dialog. We select GitHub in the Account field. It then asks us to enter GitHub’s Account and Token to log in to GitHub.

Sign in to your GitHub account.
Sign in to your GitHub account.

After entering the GitHub Account and Token, we can create a remote repository on GitHub. It is worth noting that the repository name should preferably be the same as the name of our Swift package, which is Greeting.

Create Git remote.
Create Git remote.

When Xcode creates a remote repository for us, it also push all the local commits to the remote repository.

Next, we’re going to create a Git tag to release version 1.0.0. Right-click on our last commit and select Tag “xxxxxxx”, where xxxxxxx will be replaced with the commit hash.

Create a Git tag.
Create a Git tag.

Xcode will pop up the following dialog. We enter the name of the tag, which is 1.0.0.

Create a new tag from revision.
Create a new tag from revision.

When the Git tag is created, the Git tag is only local. We need to push it to the remote repository. Select Source Control -> Push.

Source Control -> Push.
Source Control -> Push.

Xcode will pop up the following dialog. Be sure to check “Include tags”, then click the Push button. This completes the release version 1.0.0.

Push local changes including tags.
Push local changes including tags.

Publishing Swift Packages with the Command Line

If your Swift package is not a Git repository, you need to first use the following command to create a Git repository under your Swift package.

% cd Greeting
Greeting % git init
Initialized empty Git repository in /Users/wayne/Greeting/.git/
Greeting % git add .
Greeting % git commit -m "first commit"
[main (root-commit) 916bcf6] first commit
 5 files changed, 57 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Package.swift
 create mode 100644 README.md
 create mode 100644 Sources/Greeting/Greeting.swift
 create mode 100644 Tests/GreetingTests/GreetingTests.swift
Greeting % git branch -M main

Then, create a Git repository on any Git hosting. The name of the Git repository, the name of the Swift package folder, and the name of the Swift package should preferably all be the same. In the following command, we send the Swift package to https://github.com/xhhuango/Greeting.git.

Greeting % git remote add origin https://github.com/xhhuango/Greeting.git
Greeting % git push -u origin main
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 16 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (11/11), 1.41 KiB | 1.41 MiB/s, done.
Total 11 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:xhhuango/Greeting.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Finally, we use Git tag to add a tag named 1.0.0 and send the tag to the remote Git repository. This completes the release version 1.0.0.

Greeting % git tag 1.0.0
Greeting % git push origin --tags
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:xhhuango/Greeting.git
 * [new tag]         1.0.0 -> 1.0.0

Adding Swift Package Dependencies

After publishing our Swift package, you can add the newly released Swift package to your Xcode projects. The following article will detail how to add remote or local Swift packages to your Xcode projects.

Conclusion

Swift packages are reusable code components. Unlike Xcode projects, it does not require Xcode project files, but uses Package.swift to configure Swift packages. Compared to Xcode’s project files, Package.swift is much clearer and simpler.

References

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like