Swift packages are a powerful way to modularize your code, enabling you to manage dependencies, share code across multiple projects, and organize your codebase more effectively. While tools like Xcode provide a graphical interface for handling Swift packages, you might prefer or need to manage and edit Swift packages directly from the command line. This approach is beneficial when automating tasks, working on remote servers, or integrating with CI/CD pipelines.
This article walks you through the creation of a Swift package using the command line.
Prerequisites
Before diving in, ensure that you have the following tools installed:
On MacOS
- Swift 6.0, included in the latest version of Xcode 16 (currently in Beta version)
- Command line tools, are downloadable at the More downloads section of the Apple developer portal.
Note: The Package Editor Commands have been introduced by SE-0301 and implemented in Swift 6.
Creating a Swift package
To start editing a Swift package, you first need a package. If you don't have one already, you can create a new Swift package using the Swift Package Manager (SPM).
- Open your terminal
- Create and navigate to the directory where you want to create the package.
mkdir EditSwiftPackage
cd EditSwiftPackage
- Run the following command to create a new Swift package:
swift package init --type executable
the command will output a new Package with a basic main code:
Creating executable package: EditSwiftPackage
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/main.swift
Let's run it to check that it works:
swift run
After the build output, it will output:
Hello, world!
Great, it works!
Opening the package we'll have the following structure:
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EditSwiftPackage",
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "EditSwiftPackage"),
]
)
Adding a Product to the Package.swift
A package product defines an externally visible build artifact that’s available to clients of a package. *
The add-product
command help can be obtained with the following command:
swift package add-product --help
output:
OVERVIEW: Add a new product to the manifest
USAGE: swift package add-product <name> [--type <type>] [--targets <targets> ...]
ARGUMENTS:
<name> The name of the new product
OPTIONS:
--type <type> The type of target to add, which can be one of 'executable', 'library', 'static-library', 'dynamic-library', or 'plugin' (default: library)
--targets <targets> A list of targets that are part of this product
--version Show the version.
-h, -help, --help Show help information.
Let's add a product with the name EditSwiftPackage
:
swift package add-product EditSwiftPackage --type executable --targets EditSwiftPackage
The command will produce the following output:
Updating package manifest at Package.swift... done.
This is the new Package.swift
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EditSwiftPackage",
products: [
.executable(
name: "EditSwiftPackage",
targets: [ "EditSwiftPackage" ]
),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "EditSwiftPackage"),
]
)
we can test it by running:
swift run EditSwiftPackage
Adding a Dependency to the Swift Package
We want to build a command line tool, to achieve our goal we will add the Swift Argument Parser
dependency which implements a type-safe argument parser.
The add-dependency
command help can be obtained with the following command:
swift package add-dependency --help
output:
OVERVIEW: Add a package dependency to the manifest
USAGE: swift package add-dependency <dependency> [--exact <exact>] [--revision <revision>] [--branch <branch>] [--from <from>] [--up-to-next-minor-from <up-to-next-minor-from>] [--to <to>]
ARGUMENTS:
<dependency> The URL or directory of the package to add
OPTIONS:
--exact <exact> The exact package version to depend on
--revision <revision> The specific package revision to depend on
--branch <branch> The branch of the package to depend on
--from <from> The package version to depend on (up to the next major version)
--up-to-next-minor-from <up-to-next-minor-from>
The package version to depend on (up to the next minor version)
--to <to> Specify upper bound on the package version range (exclusive)
--version Show the version.
-h, -help, --help Show help information.
We want to add the package from the URL https://github.com/apple/swift-argument-parser
and we want to make sure that our dependency has a minimum version from 1.3.0
up to the next major version.
swift package add-dependency https://github.com/apple/swift-argument-parser --from 1.3.0
The command will produce the following output:
Updating package manifest at Package.swift... done.
This is the new Package.swift
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EditSwiftPackage",
products: [
.executable(
name: "EditSwiftPackage",
targets: [ "EditSwiftPackage" ]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "EditSwiftPackage"),
]
)
we can test it by running:
swift run EditSwiftPackage
Adding a target dependency
We have introduced a new dependency, but to import it from our target we need to add it to the target.
The add-target-dependency
command help can be obtained with the following command:
swift package add-target-dependency --help
output:
OVERVIEW: Add a new target dependency to the manifest
USAGE: swift package add-target-dependency <dependency-name> <target-name> [--package <package>]
ARGUMENTS:
<dependency-name> The name of the new dependency
<target-name> The name of the target to update
OPTIONS:
--package <package> The package in which the dependency resides
--version Show the version.
-h, -help, --help Show help information.
We want to add the ArgumentParser
from the package swift-argument-parser
to the EditSwiftPackage
.
swift package add-target-dependency ArgumentParser EditSwiftPackage --package swift-argument-parser
The command will produce the following output:
Updating package manifest at Package.swift... done.
This is the new Package.swift:
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EditSwiftPackage",
products: [
.executable(
name: "EditSwiftPackage",
targets: [ "EditSwiftPackage" ]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "EditSwiftPackage",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]),
]
)
we can test it by running:
swift run EditSwiftPackage
Implementing the command line:
Now, We have all the requirements to start our first and basic command line example, which will prove that our target can import the ArgumentParser
dependency.
Rename the file main.swift
to EditSwiftPackage.swift
and update the content with the following:
import ArgumentParser
@main
struct EditSwiftPackage: ParsableCommand {
@Argument(help: "The name you want to greet")
var name: String
mutating func run() throws {
print("Hello \(name)!")
}
}
You have created a command line tool in Swift!
To test it run:
swift run EditSwiftPackage
The command will produce an error:
Error: Missing expected argument '<name>'
USAGE: edit-swift-package <name>
ARGUMENTS:
<name> The name you want to greet
OPTIONS:
-h, --help Show help information.
Let's add the name parameter to run the command:
swift run EditSwiftPackage Andrea
This will produce the following output:
Hello Andrea!
Add a new Target
Similarly it's possible to add a target with the command add-target add-target
swift package add-target --help
output:
OVERVIEW: Add a new target to the manifest
USAGE: swift package add-target <name> [--type <type>] [--dependencies <dependencies> ...] [--url <url>] [--path <path>] [--checksum <checksum>] [--testing-library <testing-library>]
ARGUMENTS:
<name> The name of the new target
OPTIONS:
--type <type> The type of target to add, which can be one of 'library', 'executable', 'test', or 'macro' (default: library)
--dependencies <dependencies>
A list of target dependency names
--url <url> The URL for a remote binary target
--path <path> The path to a local binary target
--checksum <checksum> The checksum for a remote binary target
--testing-library <testing-library>
The testing library to use when generating test targets, which can be one of 'xctest', 'swift-testing', or 'none' (default: xctest)
--version Show the version.
-h, -help, --help Show help information.
Let's add the tests to our package:
swift package add-target EditSwiftPackageTests --type test --dependencies EditSwiftPackage
This is the new Package.swift:
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EditSwiftPackage",
products: [
.executable(
name: "EditSwiftPackage",
targets: [ "EditSwiftPackage" ]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "EditSwiftPackage",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]),
.testTarget(
name: "EditSwiftPackageTests",
dependencies: [ "EditSwiftPackage" ]
),
]
)
Recap
Here is a quick recap of all the commands used to create a package from the command line:
# Create a swift package
mkdir EditSwiftPackage
cd EditSwiftPackage
swift package init --type executable
# Add a Product
swift package add-product EditSwiftPackage --type executable --targets EditSwiftPackage
# Add a Dependency
swift package add-dependency https://github.com/apple/swift-argument-parser --from 1.3.0
# Add a Target dependency
swift package add-target-dependency ArgumentParser EditSwiftPackage --package swift-argument-parser
# Add a test Target
swift package add-target EditSwiftPackage-tests --type test --dependencies EditSwiftPackage
Conclusions
Editing a Swift package from the command line is a straightforward process that provides flexibility, especially when integrating with automation tools or working in environments without a graphical interface. By understanding how to navigate and manipulate your package's files, dependencies, and settings directly from the terminal, you gain greater control over your development workflow.
The new commands are additive only, this is enough to create a package and add Product, Target, Dependencies and Target dependencies. This new way of interacting with SPM could be handy to automate a workflow or to set up a template project.
Thanks for reading!