I recently discovered, while setting up my first Apple Silicon Mac, that Xcode does not have access to your shell environment. But there’s one caveat to that. (Thanks to Boris for confirming!) This post will hopefully be a reminder to my future self when I encounter this issue again.

I realized the problem when an Xcode build failed because a Run Script Build Phase that runs SwiftLint failed because the SwiftLint binary could not be found. I have SwiftLint installed via Homebrew. I knew SwiftLint was in my PATH because I could run it successfully on the command line. This meant Xcode did not have access to my PATH (and, as I learned later, my entire shell environment).

This only started to happen after switching from an Intel Mac to an Apple Silicon Mac — and that was the issue. Homebrew installs at different locations for Intel (/usr/local) and Apple Silicon (/opt/homebrew). Invoking SwiftLint from a Run Script Build Phase in Xcode on Intel Macs just happens to work because /usr/local is part of your default PATH. To fix the issue on Apple Silicon Macs, you need to tell Xcode where to look for Homebrew packages in your script.

export PATH="$PATH:/opt/homebrew/bin"

* * *

Anyway, the moral of this story is that Xcode does not have access to your shell environment. This is not necessarily an obvious thing, especially on Intel machines. Xcode only gets whatever the default PATH is, configured via launch services — unless you launch it through a terminal. What this means is that xcodebuild run from terminal and Xcode could end up with potentially different behavior in their Run Script Build Phases because they expose different environments. Good to know!

Update 15 December 2023

Thanks to Dave for linking to this post this week and sharing a very relevant tip on this topic:

Don’t forget you can still set environment variables from your project’s scheme configuration. Edit your scheme from the Product menu, select the Arguments tab against the Run behaviour, and set Environment Variables.