Xcode Run Script Build Phase Tip

The standard way to do a Run Script build phase is to just put the text of the script in the window that opens up when you double-click the build phase. But, this provides a pretty meager editing experience: no syntax highlighting, smart indenting, or anything like that. Furthermore, the contents of the script is stored directly in the project file (specifically in MyProject.xcodeproj/project.pbxproj), which makes it a bummer when it comes time to do any diffing or merging with your SCM system. Also, if you want to include the phase in multiple targets, you have to copy/paste the script to each target, and remember to update them all manually if you make a change to the script.

My trick is to put the body of the script in a normal file and call it from the Run Script build phase. It’s pretty simple, but can be quite handy.

Here is how I do this. First, make a new directory in your project folder named “scripts” and in that make a new file named something like my_build_phase_script.sh. Drag this whole new folder (“scripts”) to Xcode to add it project, using the following settings. (Be sure to un-check the “Add To Targets” boxes, because adding to the target will just copy it to the Resources folder when building.)

Next, edit the contents of your script. This is left as an exercise for the reader.

Finally, make the build phase. Right-click on your target and select Add > New Build Phase > New Run Script Build Phase. In the window that opens up, just put this for the “Script” field:

script_file="my_build_phase_script.sh"

echo "Running a custom build phase script: $script_file"
${PROJECT_DIR}/scripts/${script_file}
scriptExitStatus=$?
echo "DONE with script: ${script_file} (exitStatus=${scriptExitStatus})\n\n"
exit "${scriptExitStatus}"

Set the rest of the options like this:

Now you can edit this script using nice syntax highlighting, easily diff and merge changes, and call the same script from multiple targets without any error-prone manual updating, etc. Win-win-win.

A note for advanced users

As a build phase script, this only gets run while building (not cleaning). So, even though there is an ACTION environment variable that gets set, it isn’t much use because the script doesn’t even get run when you’re not building. There are a few fun things you can do to work around this, but I’ll leave that for another post.