Caching SwiftPM and DerivedData on macOS runners
On an ephemeral runner nothing survives between jobs by design, so caching is the only thing standing between you and a full dependency resolution on every run. On Manzanita, actions/cache works exactly as on GitHub-hosted runners; there is no vendor-specific cache to configure.
The configuration
- uses: actions/cache@v4
with:
path: |
.build
~/Library/Developer/Xcode/DerivedData
~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-${{ runner.arch }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-spm-
Package.resolvedin the key means a dependency bump invalidates the cache and anything else restores it.runner.archkeeps arm64 and x64 caches apart if you still have an Intel lane.restore-keyslets a partial hit warm the build after a bump instead of starting from zero.
What not to cache
- The whole of
~/Library/Developer/Xcode. It contains simulator state and logs that grow without bound and invalidate for no useful reason. - Anything keyed only on the branch name. Two commits on the same branch with different dependencies will silently share a stale cache.
- Build products for a different Xcode. Include the Xcode version in the key if you run more than one label:
-xcode26-or read it fromxcodebuild -versionin a prior step.
Expect these numbers
Cache restore on a Manzanita runner runs at GitHub's cache-service speed, so a 1–2 GB DerivedData restore is typically 20–40 s. A full resolution and build of a mid-sized SwiftPM app on the 6-vCPU Standard profile is usually 2–5 minutes cold; with a warm cache the same build is often under a minute. Your own numbers are on the Usage page per repository.
Provider-managed cache volumes
Persistent per-organization cache volumes are not yet part of the Manzanita product. When they are, they will be additive; the configuration above will keep working.
runs-on to manzanita-standard and keep everything else. Start free · Read the docs