Why is xcodebuild slower than the Xcode GUI?
There is a bug, but also it’s designed to be that way
Since I’ve switched to Cursor for most of my iOS development workflow, I’ve been dealing less with the Xcode GUI itself and more with its command line tool, xcodebuild.
If you’re unfamiliar with it, this command line is used to build the Xcode project without launching the Xcode app itself. If you’re running your project on a CI, and/or using Fastlane you definitely used it, even indirectly.
It’s even more prevalent when trying to build your project outside of Xcode, like using an external editor and emitting xcodebuild command to build your project.
xcodebuild ARCHS\=arm64 VALID_ARCHS\=arm64 ONLY_ACTIVE_ARCH\=NO -scheme IceCubesApp -configuration Debug -workspace /Users/dimillian/Documents/Dev/Other/IceCubesApp/IceCubesApp.xcodeproj/project.xcworkspace -destination 'platform=iOS Simulator,id=E99971ED-7594-459D-A77E-DF735E47F5B2' -resultBundlePath '/Users/dimillian/Library/Application Support/Cursor/User/workspaceStorage/ceb1a744769b70b7bfd7b78ac51e31d4/sweetpad.sweetpad/bundle/IceCubesApp' -allowProvisioningUpdates build
open -a Simulator
xcrun simctl install E99971ED-7594-459D-A77E-DF735E47F5B2 '/Users/dimillian/Library/Developer/Xcode/DerivedData/IceCubesApp-dckoljloxttnqcekprljivqttvuw/Build/Products/Debug-iphonesimulator/Ice Cubes.app'
xcrun simctl launch --console-pty --terminate-running-process E99971ED-7594-459D-A77E-DF735E47F5B2 com.thomasricouard.IceCubesApp
And then chaining it with xcrun to run it.
Unless you’re using a custom build toolchain like Bazel, you’re tied to the Xcode GUI or xcodebuild to build your project.
If, like me, you’ve been noticing that xcodebuild is slow, worry not. You’re not the only one. There is currently a bug in xcodebuild. It basically does inline network queries when building your project to check for provisioning, even on incremental builds.
The step just after Resolving Package Graph
is GatherProvisioningInputs
and the time xcodebuild spends there seems to grow with the number of targets you have in your project.
There is a way around it, as noted in this xcodebuild vim extension GitHub issue:
You can prevent xcodebuild from pinging home by blocking developerservices2.apple.com
from being accessed from your /etc/hosts
.
sudo bash -c "echo '127.0.0.1 developerservices2.apple.com' >>/etc/hosts"
And then once the build is done, restore the access
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
Note that while developerservices2.apple.com
is blocked, Xcode won’t be able to access your provisioning profiles and the GUI will remove your developers accounts if you launch it. So remember to remove it from your /etc/hosts
eventually.
This bug is present since the first beta of Xcode 16.0 and is still present in the Xcode 16.1 RC. Let’s hope Apple will fix it soon…
Rudrank also wrote a script that I’ll embed here:
Script:
#!/bin/bash
# Function to block the domain
block_domain() {
echo "127.0.0.1 http://developerservices2.apple.com" | sudo tee -a /etc/hosts > /dev/null
echo "Domain blocked"
}
# Function to unblock the domain
unblock_domain() {
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
echo "Domain unblocked"
}
# Check if xcodebuild command is provided
if [ $# -eq 0 ]; then
echo "Please provide the xcodebuild command as arguments"
exit 1
fi
# Block the domain
block_domain
# Run xcodebuild with all passed arguments
"$@"
# Capture the exit code of xcodebuild
BUILD_RESULT=$?
# Unblock the domain
unblock_domain
# Exit with the same code as xcodebuild
exit $BUILD_RESULT
Basically, it’s a wrapper for xcodebuild that will block/unblock the Apple domain while building.
Usage:
./xcodebuild-wrapper.sh xcodebuild [usual xcodebuild arguments]
But also, it’s designed that way
Well, Xcode is designed that way. Basically, xcodebuild is a one-off; the server is killed as soon as your build is done. The Xcode app works differently. It’s not directly using xcodebuild but instead a host of (private) APIs that keep the build service warm, hence why incremental builds are faster when building from the Xcode GUI. Hitting the build & run button will always be faster than running yet another xcodebuid command.
As noted from the tuist.io cache page, they optimize things around xcodebuild, but there is only so much we can do unless Apple gives us the tool they use within Xcode.
I was joking that maybe the solution is to code an extension for VSCode that actually hits that Xcode build & run button when you want to run your app. Maybe…