Install
curl -fsSL https://swx.quadratic.xyz/install.sh | sh
The script downloads the release for your OS and CPU, checks it against the release's
SHA256SUMS, and installs swx and swift-x into
~/.local/bin. With swift-x on your PATH,
swift x build works too. SWX_VERSION=v0.1.0 pins a release and
SWX_INSTALL_DIR changes the location.
From source, with Swift 6.1 or later:
git clone https://github.com/jeslyvarghese/swx && cd swx
swift build -c release
cp .build/release/swx ~/.local/bin/
ln -s swx ~/.local/bin/swift-x # optional: enables `swift x …`
swx runs SwiftPM for builds, so a Swift toolchain must be installed (or let swx toolchain install fetch one).
Dependencies
A drop-in for swift package resolve and update, with the same flags. Any other package subcommand is forwarded to SwiftPM.
swx package resolve
swx package update
swx package show-dependencies # forwarded to `swift package`
The result is what SwiftPM would have written: the same Package.resolved, the
same .build/checkouts and .build/workspace-state.json. A later
swift build finds everything resolved and does no network work.
Build, test, run
swx resolves first, then hands over to SwiftPM with resolution disabled.
swx build -c release swx test swx run MyTool --flag
SWX_BUILD_CACHE=1 shares compiled dependencies between projects through the
compiler's content-addressed cache (with SwiftPM's native build system): a second project
that uses swift-collections built in 1.7 s instead of 6.4 s.
Apple platforms
Packages that declare apps with AppleProductTypes (.iOSApplication(…),
as Xcode and Swift Playgrounds read them) build, run and test on simulators and devices.
swx uses xcodebuild, simctl and devicectl underneath,
so this needs macOS with Xcode.
swx devices # simulators and paired devices swx build --platform ios # generic build when no simulator exists swx run --platform ios HelloApp # build, install, launch, stream console swx run --platform ios --destination device --team ABCDE12345 HelloApp swx test --platform watchos # package tests on a watchOS simulator
Platforms: ios, watchos, tvos, visionos.
Xcode projects
Xcode's own resolution can't be replaced, but it can be fed. swx xcode resolve
prepares SourcePackages exactly as Xcode would (same pins, originHash
and state), including projects with registries, mirrors and local package folders.
swx xcode resolve --source-packages SourcePackages
xcodebuild -clonedSourcePackagesDirPath SourcePackages \
-disableAutomaticPackageResolution -onlyUsePackageVersionsFromResolvedFile …
When an app outgrows a package, swx xcode generate writes an .xcodeproj for its .iOSApplication products (through XcodeGen, fetched like any swx tool).
Toolchains
swift.org releases and snapshots, installed per user. Every command honours .swift-version and installs the pinned toolchain on demand.
swx toolchain list --available swx toolchain install 6.4 swx toolchain install main-snapshot # also 6.5-snapshot, or a snapshot tag swx toolchain use 6.4 # writes .swift-version swx toolchain which
Tools
Run executables from packages without installing them, like uvx or Mint. Builds are cached per revision and toolchain, so a pinned tool starts in about 20 ms.
swx tool run swiftformat@0.54.0 --version
swx tool run realm/SwiftLint lint
swx tool install swiftlint # links into ~/.local/bin
Scripts
Single-file scripts declare their dependencies in a comment; products are inferred from the imports. A warm start takes about 10 ms.
#!/usr/bin/env -S swx run // swx-dependencies: // .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0") import ArgumentParser
swx run greet.swift Ada
Environment
| Variable | Effect |
|---|---|
SWX_CACHE_DIR | The shared store. Default ~/.cache/swx, or $XDG_CACHE_HOME/swx. |
SWX_HOME | Toolchains. Default ~/.local/share/swx. |
SWX_TRACE=1 | A timestamped timeline of every subprocess. |
SWX_STATIC_MANIFESTS=0 | Always evaluate manifests with the compiler. |
SWX_GIT_HTTP=0 | List tags with git ls-remote instead of HTTP. |
SWX_BUILD_CACHE | 0 disables the shared compilation cache; 1 opts into it with SwiftPM's native build system. |
Compatibility
Each of these is checked byte for byte against SwiftPM or Xcode:
- Package.resolved: v2 and v3 written, v1 read;
originHashmatches SwiftPM's and Xcode's. - Workspace state:
workspace-state.jsonv7, including path dependencies and binary artifacts. Entries swx doesn't manage (edited packages, prebuilts) are kept untouched. - Dependencies: branch, revision, path and local git dependencies; SwiftPM's pruning, traits, tag-to-version rules and pre-release ranges.
- Mirrors:
mirrors.jsonfrom the package and~/.swiftpm; the original URL is what the lockfile records. - Submodules and Git LFS: checked out as SwiftPM checks them out (LFS needs
git-lfs, as SwiftPM does). - Registries:
registries.json, SwiftPM's credential lookup (environment, Keychain,.netrc), checksums and trust-on-first-use fingerprints, SE-0391 signatures, and--replace-scm-with-registry. - Tag fingerprints: the commit behind each tag is recorded in SwiftPM's store, and a tag that moves is refused.
- Binary targets: XCFrameworks, artifact bundles and
.artifactbundleindex, downloaded and unzipped once per machine. - Manifests: 0 disagreements with
dump-packageover a 364-manifest corpus. - Xcode: projects, workspaces and root package folders, with the registries and mirrors Xcode keeps in
xcshareddata. - Mixed use:
swift package updateworks on top of swx checkouts, and swift-syntax prebuilts still apply.
Multi-root workspaces are not supported.
Verification
Compatibility is checked differentially: each script runs SwiftPM or Xcode and swx on the same input and compares what they write. The full list is in the contributing guide; for example:
Scripts/compare-with-swiftpm.sh <package-dir> # Package.resolved, workspace-state.json, checkouts Scripts/compare-registry-signing.sh # signed registry releases: trusted, untrusted, tampered Scripts/compare-xcode-apps.sh # an app + widget + watch app project vs xcodebuild swx debug manifest-parity --cache-path <dir> # the static parser vs dump-package over a corpus
Found a difference from SwiftPM? That's a bug — please report it.