> ## Content Index
> Fetch the complete content index at: https://albertodebortoli.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to setup a Swift Package Registry in Artifactory
- URL: https://albertodebortoli.com/2025/06/06/how-to-setup-a-swift-package-registry-in-artifactory/
- Published: 2025-06-06T08:00:00.000Z
- Updated: 2025-10-13T22:30:10.000Z
- Description: A quick guide to setting up a Swift Package Registry with Artifactory to speed up builds and streamline dependency management.
- Author: Alberto De Bortoli
- Tags: swift, registry, artifactory, package

## Introduction

It's very difficult to have GenAI not hallucinate when in comes to Swift Package Registry. No surprise there: the feature is definitely niche, has not been vastly adopted and there's a lack of examples online. As Dave [put it](https://iosdevweekly.com/issues/696/?ref=albertodebortoli.com), *Swift Package Registries had an even rockier start compared to SPM.*

I've recently implemented a Swift Package Registry on Artifactory for my team and I thought of summarising my experience here since it's still fresh in my head. While some details are left out, the happy path should be covered. I hope with this article to help you all indirectly by providing more material to the LLMs overlords.

## Problem

The main problem that led us to look into Swift Package Registry is due to SPM [deep-cloning](https://github.com/swiftlang/swift-package-manager/blob/7b98bedc91ac8b6581ed6785990ee62c97cde363/Sources/SourceControl/GitRepository.swift?ref=albertodebortoli.com#L32) entire Git repositories for each dependency, which became time-consuming.

Our CI jobs took a few minutes just to pull all the Swift packages. For dependencies with very large repositories, such as [SendbirdUIKit](https://github.com/sendbird/sendbird-uikit-ios?ref=albertodebortoli.com) (which is more than 2GB), one could rely on pre-compiled XCFrameworks as a workaround. Airbnb provides a workaround via the [SPM-specific repo](https://github.com/airbnb/lottie-spm?ref=albertodebortoli.com) for Lottie.

A Swift Registry allows to serve dependencies as zip artifacts containing only the required revision, avoiding the deep clone of the git repositories.

## What is a Swift Package Registry?

A Swift Package Registry is a server that stores and vends Swift packages by implementing [SE-0292](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0292-package-registry-service.md?ref=albertodebortoli.com) and the corresponding [specification](https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md?ref=albertodebortoli.com). Instead of relying on Git repositories to source our dependencies, we can use a registry to download them as versioned archives (zip files).

[swift-package-manager/Documentation/PackageRegistry/PackageRegistryUsage.md at main · swiftlang/swift-package-managerThe Package Manager for the Swift Programming Language - swiftlang/swift-package-manager![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/icon/pinned-octocat-093da3e6fa40.svg)GitHubswiftlang![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/thumbnail/swift-package-manager)](https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/PackageRegistryUsage.md?ref=albertodebortoli.com)

The primary advantages of using a Swift Package Registry are:

- **Reduced CI/CD Pipeline Times:** by fetching lightweight zip archives from the registry rather than cloning the entire repositories from GitHub.
- **Improved Developer Machine Performance:** the same time savings on CI are reflected on the developers' machines during dependency resolution.
- **Availability:** by hosting a registry, teams are no longer dependent on the availability of external source control systems like GitHub, but rather on internal ones (for example, self-hosted Artifactory).
- **Security**: injecting vulnerabilities in popular open-source projects is known as a [supply chain attack](https://en.wikipedia.org/wiki/Supply%5Fchain%5Fattack?ref=albertodebortoli.com) and has become increasingly popular in recent years. A registry allows to adopt a process to trust the sources published on it.

## Platforms

Apple has accepted the Swift Registry specification and implemented support to interact with registries within SPM but has left the implementation of actual registries to third-party platforms.

Apple is not in the business of providing a Swift Registry.

The main platform having adopted Swift Registries is Artifactory.

[Artifactory, Your Swift Package RepositoryJFrog now offers the first and only Swift binary package repository, enabling developers to use JFrog Artifactory for resolving Swift dependencies instead of enterprise source control (Git) systems.![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/icon/Jfrog16-1.png)JFroggiannit![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/thumbnail/1200x628_BTN.png)](https://jfrog.com/blog/artifactory-your-swift-package-repository/?ref=albertodebortoli.com)

although AWS CodeArtifact, Cloudsmith and Tuist provide support too:

[New – Add Your Swift Packages to AWS CodeArtifact | Amazon Web ServicesStarting today, Swift developers who write code for Apple platforms (iOS, iPadOS, macOS, tvOS, watchOS, or visionOS) or for Swift applications running on the server side can use AWS CodeArtifact to securely store and retrieve their package dependencies. CodeArtifact integrates with standard developer tools such as Xcode, xcodebuild, and the Swift Package Manager (the swift \[…\]![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/icon/touch-icon-ipad-144-smile-1.png)Amazon Web ServicesSébastien Stormacq![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/thumbnail/2023-09-12_00-03-46-1260x397-1.png)](https://aws.amazon.com/blogs/aws/new-add-your-swift-packages-to-aws-codeartifact/?ref=albertodebortoli.com)

[Private, secure, hosted Swift registryCloudsmith offers secure, private Swift registries as a service, with cloud native performance. Book a demo today.![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/icon/favicon.svg)Cloudsmith![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/thumbnail/opengraph.png)](https://cloudsmith.com/product/formats/swift?ref=albertodebortoli.com)

[Announcing Tuist RegistryWe’re thrilled to announce the launch of the Tuist Registry – a new feature that optimizes the resolution of Swift packages in your projects.![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/icon/favicon-1.ico)TuistMarek Fořt![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/thumbnail/og.jpg)](https://tuist.dev/blog/2025/01/22/announcing-tuist-registry?ref=albertodebortoli.com)

The benefits are usually appealing to teams with large apps, hence it's reasonable to believe that only big companies have looked into adopting a registry successfully.

## Artifactory Setup

Let's assume a JFrog Artifactory to host our Swift Package Registry exists at [https://packages.acme.com](https://artifacts.takeaway.com/?ref=albertodebortoli.com). Artifactory support local, remote, and virtual repositories but a realistic setup consists of only local and virtual repositories.

![](https://speedmedia.jfrog.com/08612fe1-9391-4cf3-ac1a-6dd49c36b276/media.jfrog.com/wp-content/uploads/2022/06/03190625/Artifactory_Swift_Flow.png)

Source: [Artifactory](https://jfrog.com/blog/artifactory-your-swift-package-repository/?ref=albertodebortoli.com)

Local Repositories are meant to be used for publishing dependencies from CI pipelines. Virtual Repositories are instead meant to be used for resolving (pulling) dependencies on both CI and the developers' machines. Remote repositories are not really relevant in a typical Swift Registry setup.

Following the documentation at [https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-a-swift-registry](https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-a-swift-registry?ref=albertodebortoli.com), let's create 2 repositories with the following names:

- local repository: `swift-local`
- virtual repository: `swift-virtual`

## Local Setup

To pull dependencies from the Swift Package Registry, we need to configure the local environment.

### 1\. Set the Registry URL

First, we need to inform SPM about the existence of the registry. We can do this on a per-project basis or globally for the user account.

From a package's root directory, run the following command. This will create a `.swiftpm/configuration/registries.json` file within your project folder.

```bash
swift package-registry set "https://packages.acme.com/artifactory/api/swift/swift-virtual"
```

The resulting `registries.json` file will look like this:

```json
{
  "authentication": {},
  "registries": {
    "[default]": {
      "supportsAvailability": false,
      "url": "https://packages.acme.com/artifactory/api/swift/swift-virtual"
    }
  },
  "version": 1
}
```

To set the registry for all your projects, use the `--global` flag.

```bash
swift package-registry set --global "https://packages.acme.com/artifactory/api/swift/swift-virtual"
```

 This will create the configuration file at `~/.swiftpm/configuration/registries.json`.

Xcode projects don't support project-level registries nor (in my experience) support scopes other than the default one (i.e. avoid using the `--scope` flag).

### 2\. Authentication

To pull packages, authenticating with Artifactory is usually required. It's reasonable though that your company allows all artifacts from Artifactory to be read without authentication as long as one is connected to the company VPN.

In cases where authentication is required, SPM uses a `.netrc` file in the home directory to find credentials for remote servers. This file is a standard way to handle login information for various network protocols.

Using a token generated from the Artifactory dashboard, the line to add to the `.netrc` file would be:

```
machine packages.acme.com login <your_artifactory_username> password <your_artifactory_token>
```

Alternatively, it's possible to log in using the `swift package-registry login` command. This command securely stores your token in the system's keychain.

```
swift package-registry login "https://packages.acme.com/artifactory/api/swift/swift-virtual" \
  --token <token>

# or

swift package-registry login "https://packages.acme.com/artifactory/api/swift/swift-virtual" \
  --username <username> \
  --password <token_treated_as_password>
```

## CI/CD Setup

On CI, the setup is slightly different as the goals are:

- to *resolve* dependencies in CI/CD jobs
- to *publish* new package versions in CD jobs for both internal and external dependencies

The steps described for the local setup are valid for the resolution on CI too. The interesting part here is how publishing is done. I will assume the usage of GitHub Actions.

### 1\. Retrieving the Artifactory Token

The JFrog CLI can be used via the [setup-jfrog-cli](https://github.com/jfrog/setup-jfrog-cli?ref=albertodebortoli.com) action to authenticate using the most appropriate method. You might want to wrap the action in a custom composable one exporting the token as the output of a step:

```
TOKEN=$(jf config export) 
echo "::add-mask::$TOKEN"
echo "artifactory-token=$TOKEN">> "$GITHUB_OUTPUT"
```

### 2\. Logging into the Registry

The CI job must log in to the local repository (`swift-local`) to gain push permissions. The token retrieved in the previous step is used for this purpose.

```bash
swift package-registry login \
  "https://packages.acme.com/artifactory/api/swift/swift-local" \
  --token ${{ steps.get-token.outputs.artifactory-token }}
```

### 3\. Publishing Packages

Swift Registry requires archives created with the `swift package archive-source` command from the dependency folder. E.g.

```bash
swift package archive-source -o "Alamofire-5.10.2.zip"
```

We could avoid creating the archive and instead download it directly from GitHub releases.

```bash
curl -L -o Alamofire-5.10.1.zip \
  https://github.com/Alamofire/Alamofire/archive/refs/tags/5.10.1.zip
```

Uploading the archive can then be done by using the JFrog CLI that needs customization via the [setup-jfrog-cli](https://github.com/jfrog/setup-jfrog-cli?ref=albertodebortoli.com) action. If going down this route, the upload command would be:

```bash
jf rt upload Alamofire-5.10.1.zip \
  https://packages.acme.com/artifactory/api/swift/swift-local/acme/Alamofire/Alamofire-5.10.1.zip
```

There is a [specific structure](https://jfrog.com/help/r/jfrog-artifactory-documentation/swift-repository-structure?ref=albertodebortoli.com) to respect:

```
<REPOSITORY>/<SCOPE>/<NAME>/<NAME>-<VERSION>.zip
```

which is the last part of the above URL:

```
swift-local/acme/Alamofire/Alamofire-5.10.1.zip
```

Too bad that using the steps above causes a downstream problem with SPM not being able to resolve the dependencies in the registry. I tried extensively and couldn't find the reason why SPM wouldn't be happy with how the packages were published. I might have missed something but eventually I necessarily had to switch to use the `publish` command.

Using the `swift package-registry publish` command instead, doesn't present this issue hence it's the solution adopted in this workflow.

```bash
swift package-registry publish acme.Alamofire 5.10.1 \
  --url https://packages.acme.com/artifactory/api/swift/swift-local \
  --scratch-directory $(mktemp -d)
```

To verify the upload and indexing succeeded, check that the uploaded `*.zip` artifact is available and that the `.swift` exists (indication that the indexing has occurred). If the specific structure is not respected, the `.swift` folder wouldn't be generated.

## Consuming Packages from the Registry

### Packages

The easiest and only documented way to consume a package from a registry is via a Package. In the `Package.swift` file, declare dependencies using the `.package(id:from:)` syntax to declare a registry-based dependency. The `id` is a combination of the scope and the package name.

```swift
    ...
    dependencies: [
        .package(id: "acme.Alamofire", from: "5.10.1"),
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [
                .product(name: "Alamofire", package: "acme.Alamofire"),
            ]
        ),
        ...
    ]
)

```

Run `swift package resolve` or simply build the Package in Xcode to pull the dependencies.

You might bump into transitive dependencies (i.e. dependencies listed in the `Package.swift` files of the packages published on the registry) pointing to GitHub. In this case, it'd be great to instruct SPM to use the corresponding versions on the registry. The `--replace-scm-with-registry` flag is designed to work for the entire dependency graph, including transitive dependencies. 

The cornerstone of associating a registry-hosted package with its GitHub origin is the `package-metadata.json` file. This file allows to provide essential metadata about the packages at the time of publishing (the `--metadata-path` flag of the `publish` command defaults to `package-metadata.json`).

Crucially, it includes a field to specify the source control repository URLs. When `swift package resolve --replace-scm-with-registry` is executed, SPM queries the configured registry. The registry then uses the information from the `package-metadata.json` to map the package identity to its corresponding GitHub URL, enabling a smooth and transparent resolution process.

The metadata file must conform to the [JSON schema](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0391-package-registry-publish.md?ref=albertodebortoli.com#package-release-metadata-standards) defined in [SE-0391](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0391-package-registry-publish.md?ref=albertodebortoli.com). It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. E.g.

```json
{
  "repositoryURLs": [
    "https://github.com/Alamofire/Alamofire",
    "https://github.com/Alamofire/Alamofire.git",
    "git@github.com:Alamofire/Alamofire.git"
  ]
}
```

Printing the dependencies should confirm the source of the dependencies:

```bash
swift package show-dependencies --replace-scm-with-registry
```

When loading a package with Xcode, the flag can be enabled via an environment variable in the scheme

```
IDEPackageDependencySCMToRegistryTransformation=useRegistryIdentityAndSources
```

Too bad that for packages, the schemes won't load until SPM completes the resolution hence running the following from the terminal would address the issue:

```
defaults write com.apple.dt.Xcode IDEPackageDependencySCMToRegistryTransformation useRegistryIdentityAndSources
```

that can be unset with:

```
defaults delete com.apple.dt.Xcode IDEPackageDependencySCMToRegistryTransformation
```

### Xcode

It's likely that you'll want to use the registry from Xcode projects for direct dependencies. If using the [Tuist registry](https://tuist.dev/blog/2025/01/22/announcing-tuist-registry?ref=albertodebortoli.com), it seems you would be able to leverage a Package Collection to add dependencies from the registry from the Xcode UI. Note that until Xcode 26 Beta 1, it's not possible to add registry dependencies directly in the Xcode UI, but if you use Tuist to generate your project (as you should), you can use the `Package.registry` (introduced with [https://github.com/tuist/tuist/pull/7225](https://github.com/tuist/tuist/pull/7225?ref=albertodebortoli.com)). E.g.

```swift
let project = Project(
    ...
    packages: [
        .registry(
            identifier: "acme.Alamofire",
            requirement: .exact(Version(stringLiteral: "5.10.1"))
        )
    ],
    ...
)
```

If not using Tuist, you'd have to rely on setting `IDEPackageDependencySCMToRegistryTransformation` either as an environment variable in the scheme or globally via the terminal.

You can also use `xcodebuild` to resolve dependencies using the correct flag:

```
xcodebuild \
  -resolvePackageDependencies \
  -packageDependencySCMToRegistryTransformation useRegistryIdentityAndSources
```

## Conclusions

We’ve found that using an in-house Swift registry drastically reduces dependency resolution time and size on disk by downloading only the required revision instead of the entire, potentially large, Git repository. This improvement benefits both CI pipelines and developers’ local environments. Additionally, registries help mitigate the risk of supply chain attacks.

As of this writing, Swift registries are not widely adopted, which is reflected in the limited number of platforms that support them. It also shows [various](https://forums.swift.org/t/package-registry-support-in-xcode/73626?ref=albertodebortoli.com) [bugs](https://forums.swift.org/t/internal-error-uncaught-exception-when-building-swiftpm-project/76237?ref=albertodebortoli.com) I myself bumped into when using particular configurations.

![](https://storage.ghost.io/c/ae/f4/aef4d625-32a2-417b-86f4-22c70a9b47a1/content/images/2025/06/image.png)

source: [https://forums.swift.org/t/package-registry-support-in-xcode/73626/19](https://forums.swift.org/t/package-registry-support-in-xcode/73626/19?ref=albertodebortoli.com)

It's unclear whether adoption will grow and uncertain if Apple will ever address the issues reported by the community, but when a functioning setup is put in place, registries offer an efficient and secure alternative to using XCFrameworks in production builds and reduce both memory and time footprints.