# Adding Swift packages in Xcode

> Learn how to add the popular Swift networking package Alamofire in a Swift Xcode project

- Canonical: https://dennisokeeffe.com/blog/2025-10-02-adding-swift-packages-in-xcode
- Published: 2025-10-02
- Updated: 2025-10-02
- Tags: swift, beginner

This project walks through how to add third-party packages to your Swift projects in Xcode

## Prerequisites

- Swift (this post uses Swift 6.1).

## Initializing a Swift executable package

You can create a new Swift executable package with the following (from the command line):

```sh
$ mkdir hello-swift
$ cd hello-swift
$ swift package init --type executable
```

You can run `swift package init --help` to get more information around arguments like `--type`. For example:

```sh
swift package init --help
OVERVIEW: Initialize a new package

USAGE: swift package init [--type <type>] [--enable-xctest] [--disable-xctest] [--enable-swift-testing] [--disable-swift-testing] [--name <name>]

OPTIONS:
  --type <type>           Package type: (default: library)
        library           - A package with a library.
        executable        - A package with an executable.
        tool              - A package with an executable that uses
                            Swift Argument Parser. Use this template if you
                            plan to have a rich set of command-line arguments.
        build-tool-plugin - A package that vends a build tool plugin.
        command-plugin    - A package that vends a command plugin.
        macro             - A package that vends a macro.
        empty             - An empty package with a Package.swift manifest.
  --enable-xctest/--disable-xctest
                          Enable support for XCTest
  --enable-swift-testing/--disable-swift-testing
                          Enable support for Swift Testing
  --name <name>           Provide custom package name
  --version               Show the version.
  -h, -help, --help       Show help information.
```

With this new package initialized as an executable, you can navigate to `Sources/main.swift` to see the generated entry file:

```swift
/ The Swift Programming Language
// https://docs.swift.org/swift-book

print("Hello, world!")
```

## Running our executable

You can make use of `swift run` to run our executable:

```sh
$ swift run
[1/1] Planning build
Building for debugging...
[1/1] Write swift-version--58304C5D6DBC2206.txt
Build of product 'hello-swift' complete! (0.19s)
Hello, world!
```

We can re-use this to run after any file changes:

```sh
$ swift run
Building for debugging...
[7/7] Applying hello-swift
Build of product 'hello-swift' complete! (1.01s)
Hello again, world!
```

If you have a program installed such as [watchexec](https://github.com/watchexec/watchexec), you can set it to watch swift extension files with `watchexec --exts swift -- swift run
`:

```sh
$ watchexec --exts swift -- swift run
[Running: swift run]
[1/1] Planning build
Building for debugging...
[1/1] Write swift-version--58304C5D6DBC2206.txt
Build of product 'hello-swift' complete! (0.20s)
Hello again, world!
[Command was successful]
[Running: swift run]
[1/1] Planning build
Building for debugging...
[7/7] Applying hello-swift
Build of product 'hello-swift' complete! (0.73s)
Hello again and again, world!
[Command was successful]
```

## Building files

You can run `swift build` from the project root to build out the executable.

```sh
$ swift build
[1/1] Planning build
Building for debugging...
[1/1] Write swift-version--58304C5D6DBC2206.txt
Build complete! (0.23s)
```

By default, `swift build` will build with debug configuration.

Outputs are placed in `.build`:

```sh
$ tree .build -L 3
.build
├── arm64-apple-macosx
│   └── debug
│       ├── description.json
│       ├── hello_swift.build
│       ├── hello-swift
│       ├── hello-swift-entitlement.plist
│       ├── hello-swift.dSYM
│       ├── hello-swift.product
│       ├── index
│       ├── ModuleCache
│       ├── Modules
│       ├── plugin-tools-description.json
│       └── swift-version--58304C5D6DBC2206.txt
├── artifacts
├── build.db
├── checkouts
├── debug -> arm64-apple-macosx/debug
├── debug.yaml
├── plugin-tools.yaml
├── repositories
└── workspace-state.json
```

For an optimized build, pass `--configuration release`:

```sh
$ swift build --configuration release
Building for production...
[5/5] Linking hello-swift
Build complete! (1.21s)
$ tree .build -L 3
.build
├── arm64-apple-macosx
│   ├── debug
│   │   ├── description.json
│   │   ├── hello_swift.build
│   │   ├── hello-swift
│   │   ├── hello-swift-entitlement.plist
│   │   ├── hello-swift.dSYM
│   │   ├── hello-swift.product
│   │   ├── index
│   │   ├── ModuleCache
│   │   ├── Modules
│   │   ├── plugin-tools-description.json
│   │   └── swift-version--58304C5D6DBC2206.txt
│   └── release
│       ├── description.json
│       ├── hello_swift.build
│       ├── hello-swift
│       ├── hello-swift.dSYM
│       ├── hello-swift.product
│       ├── ModuleCache
│       ├── Modules
│       ├── plugin-tools-description.json
│       └── swift-version--58304C5D6DBC2206.txt
├── artifacts
├── build.db
├── checkouts
├── debug -> arm64-apple-macosx/debug
├── debug.yaml
├── plugin-tools.yaml
├── release -> arm64-apple-macosx/release
├── release.yaml
├── repositories
└── workspace-state.json

20 directories, 14 files
```

Once compiled, you'll have an optimized binary you can run directly (if your platform is the correct target):

```sh
$ ./.build/release/hello-swift
Hello again and again, world!
```

Other useful flags include `--triple` to cross-compile for another platform.

## Makefile

You can make use of a `Makefile` for common operations:

```makefile
.PHONY: build run clean release test

build:
	swift build

release:
	swift build -c release

run:
	swift run

test:
	swift test

clean:
	swift package clean
```

In use:

```sh
$ make clean
swift package clean

$ make build
swift build
Building for debugging...
[8/8] Applying hello-swift
Build complete! (1.06s)

$ make run
swift run
[1/1] Planning build
Building for debugging...
[1/1] Write swift-version--58304C5D6DBC2206.txt
Build of product 'hello-swift' complete! (0.21s)
Hello again and again, world!
```

## Managing packages

You can manage packages in `Package.swift` or via the command line.

The default file that was created:

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

import PackageDescription

let package = Package(
    name: "hello-swift",
    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: "hello-swift"),
    ]
)
```

From the command line, you can install a package using `swift package add-dependency <URL>`:

```sh
$ swift package add-dependency https://github.com/Alamofire/Alamofire --from 5.10.2
Updating package manifest at Package.swift... done.
```

In the above case, the package is installed via the GitHub repository for the popular networking package [Alamofire](https://github.com/Alamofire/Alamofire).

You must also provide a version.

```sh
$ swift package add-dependency https://github.com/Alamofire/Alamofire
error: must specify one of --exact, --branch, --revision, --from, or --up-to-next-minor-from
```

Once installed, your `Package.swift` file will be updated:

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

import PackageDescription

let package = Package(
    name: "hello-swift",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.10.2"),
    ],
    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: "hello-swift"),
    ]
)
```

In order to use it, you **must** update the relevant `targets`:

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

import PackageDescription

let package = Package(
    name: "hello-swift",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.10.2"),
    ],
    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: "hello-swift",
            dependencies: [
                "Alamofire"   // 👈 add this line
            ]),
    ]
)
```

We can see this in action by updating our `main.swift` file:

```swift
// Sources/main.swift
// The Swift Programming Language
// https://docs.swift.org/swift-book

import Alamofire
import Foundation

@main
struct Hello {
    static func main() {
        AF.request("https://jsonplaceholder.typicode.com/todos/1")
            .responseString { response in
                print(response.value ?? "no data")
                CFRunLoopStop(CFRunLoopGetMain()) // stop loop when done
            }

        // keep the program alive until CFRunLoopStop is called
        CFRunLoopRun()
    }
}
```


  In the above code, we use `CFRunLoopRun`, `CFRunLoopStop` and
  `CFRunLoopGetMain` functions for writing backwards-compatible Alamofire code.
  If you want to use async/await, you can update your `Package.swift` platforms
  to target higher versions of MacOS (assuming you are on MacOS).


Let's see it in action:

```sh
$ swift run
Fetching https://github.com/Alamofire/Alamofire
Fetched https://github.com/Alamofire/Alamofire from cache (22.64s)
Computing version for https://github.com/Alamofire/Alamofire
Computed https://github.com/Alamofire/Alamofire at 5.10.2 (23.82s)
Creating working copy for https://github.com/Alamofire/Alamofire
Working copy of https://github.com/Alamofire/Alamofire resolved at 5.10.2
[1/1] Planning build
Building for debugging...
[52/52] Applying hello-swift
Build of product 'hello-swift' complete! (6.15s)
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
```

Perfect, we got our response from an installed package.

## Conclusion

This post was recounting some baby steps with initializing Swift packages. I will likely be writing a few tools for MacOS with this approach for learning more about the language while diving into iOS.

## Links and Further Reading

- [Alamofire](https://github.com/Alamofire/Alamofire)
- [watchexec](https://github.com/watchexec/watchexec)
- [swiftlang/swift-package-manager](https://github.com/swiftlang/swift-package-manager)
- [Swift Package Manager](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/)

_Photo credit: [`johnnyb803`](https://unsplash.com/@johnnyb803)_
