Fast Xcode CI without owning a Mac.
Build, sign and test iOS apps on Apple silicon we own, a sixth of GitHub’s macOS minute, none of Xcode Cloud’s GUI lock-in. Adopt it as a drop-in macOS runner or call the build API directly, and install the result straight onto a simulator.
- per macOS minute
- $0.01
- per macOS minute
- our own racks
- Apple silicon
- our own racks
- faster iOS build
- 2×
- faster iOS build
- metering
- per second
- metering
Mac CI is slow, expensive, or walled in.
GitHub bills macOS at ten times a Linux minute. Xcode Cloud is a GUI pipeline you can’t version or port, on hardware it won’t name. A Mac mini under a desk is a single point of failure. iOS teams deserve better than all three.
- Mac ownership
The 10× macOS tax
Hosted Mac minutes are the most expensive line in most iOS pipelines. Owning the racks removes the markup.
- Platform control
Lock-in and opacity
Xcode Cloud hides the hardware and locks config in a GUI. A build should be a versioned call, and you should know the chip.
- Build performance
Cold, serial builds
Without a warm cache and warm hosts, every clean build pays the full DerivedData and SPM cost again.
Swap a label, or call the API.
Adopt it however your pipeline is shaped, a one-line runner change, or a direct build call that returns a signed artifact.
- 01
Point at our Macs
Change runs-on to sandal-macos-m4, or call sandal.builds.xcode.run from anywhere. Same Xcode, same fastlane, same signing.
- 02
Sync only the diff
Source is content-addressed; a second build ships kilobytes. DerivedData, SPM and CocoaPods restore from a warm cache keyed by your dependency graph.
- 03
Build & sign
Compile on dedicated Apple silicon in an ephemeral VM. Signing material is a grant id, decrypted into the keychain at build time and destroyed with the VM.
- 04
Ship the artifact
Get the .ipa, .xcresult and dSYM behind a signed URL or in your own bucket, or install the .app straight onto a live simulator.
A real Mac pipeline, none of the drag.
- 01
Drop-in runner
One runs-on change; every action, cache and secret keeps working.
- 02
Build API
Kick off a signed build from Linux, a queue worker or an agent, no macOS runner required.
- 03
Signing grants
Register certificates once; build requests carry an id, never the secret.
- 04
DerivedData cache
A 340k-line incremental build drops from 9m 12s to 2m 38s on a warm cache.
- 05
Build → simulator
attachTo installs and launches the fresh .app on a device for review or an agent loop.
- 06
Portable config
A build is a versioned call in your repo, not a GUI pipeline locked to one vendor.
The products that power it.
Apple CI is powered by Sandal’s Mac build and runner surfaces.
1from sandal import Sandbox
2
3# boot a desktop the agent can see
4vm = Sandbox.create(desktop=True)
5vm.screenshot("before.png")
6
7vm.click(x=412, y=280)
8vm.type("hello from the agent")
9vm.key("cmd+s")
10
11# read the screen back
12frame = vm.stream.latest()
13assert frame.contains("Saved")
➜ sandbox sandal desktop --stream
connecting to vm_7c31…
✓ stream open · 60fps · 18ms latency
screenshot → before.png
click(412, 280) · type · cmd+s
✓ saved
➜ sandbox
Move your iOS pipeline off the hosted Mac.
Apple silicon we own, a sixth of GitHub’s minute, no lock-in. Swap one line or call the API.
