Home Archive Tutorials

How To Change Your App Icon at Build Time

Using Xcode, ImageMagick and shell-scripting, this tutorial demonstrates how to generate app icons at build time based on the current build configuration.

1/5 1 Rating
Beta ribbon and build number.

Beta ribbon and build number.

So, you’ve finished a beta version of your app, and finally there are some fresh eyeballs and brains helping you test and perfect the app that’s going to make you rich…or at least fatten your portfolio.

But wouldn’t it be helpful if testers had an easy way to check which build version of the app they have?

This tutorial will show you how to do that, and perhaps introduce you to a few lesser-known features of Xcode.

Would you believe you aren’t going to write a single line of Swift in this tutorial? No, seriously, and you won’t be writing any Objective-C either. :]

This tutorial will have you writing only bash shell scripts. You’ll use a tool called ImageMagick, Terminal and Xcode to write a script that will automatically overlay the build version and a “debug” or “beta” ribbon to your app’s icon.

This tutorial assumes you have some basic knowledge of Unix scripting. You can probably follow along without being a shell scripting guru, but you can also look up what you need at Bash Beginners Guide or Advanced Bash Scripting Guide .

So, do you want get going?

Getting Started

First things first, you’ll need ImageMagick installed, which is a powerful image processing software suite that you interact with exclusively via Terminal.

You can easily install ImageMagick with Homebrew.

If you don’t have Homebrew installed or perhaps don’t know what it’s, you can learn about it and how to install from the tools homepage.

If you already have Homebrew installed, it’s still a good idea to launch Terminal and enter

brew update

This will make sure that you’re getting the latest version of any package you install from Homebrew. It will also inform you if you don’t have Homebrew installed.

OK cool, you’re all set to use Homebrew to install the packages you need. Now run the following command:

brew install ImageMagick


You should see a monologue from Homebrew that explains the installation of ImageMagick, so follow the steps to install the program.

You’ll also need to install the software suite Ghostscript, because the text functions you’ll need in ImageMagick depend on it. Ghostscript is a software suite designed for rendering PDF and PS files. You need it because it provides the font support for ImageMagick.

Install Ghostscript by running the command:

brew install ghostscript

If you encounter any difficulties later on, run this command:

brew doctor

If anything has gone awry, you’ll get a message about it, along with instructions for how to fix it.

And that’s all you need to install to be able to work through this tutorial.

Hello Llama

ImageMagick has many commands, but the two you need for this tutorial are convert and composite.

  • convert takes an image, modifies it, and then saves the result as a new image.
  • composite takes an image, overlays it on another image, and outputs the result to a third image.

This tutorial provides some sample icons for you to use. Of course, you may use your own, but you’ll need to adapt the file names accordingly. Download the sample icons, and for the purposes of this tutorial, place them on the desktop.

One of the goals here is to overlay the build version on the app’s icon. So you’ll see how to use ImageMagick to overlay text on an image by putting Hello World on one of these icons. Open Terminal and navigate to the app icon folder:

cd ~/Desktop/AppIconSet

Now type:

convert AppIcon60x60@2x.png -fill white -font Times-Bold -pointsize 18 -gravity south -annotate 0 "Hello World" test.png

I’ll break this command down for you, parameter by parameter, so you understand what’s going on:

  • AppIcon60x60@2x.png is the input image’s file name;
  • fill white sets the text’s fill color to white;
  • font Times-Bold instructs ImageMagick to use the bold variant of the Times typeface for any text;
  • pointsize 18 sets the font size to 18 pixels;
  • gravity south means any text generated by the command will be aligned at the bottom of the image
  • annotate 0 “Hello World” tells ImageMagick to annotate the image with the text Hello World at an angle of 0 degrees;
  • test.png is the name of the output file, and ImageMagick will overwrite the existing file if it already exists.

If you didn’t see any errors on the screen, you will now see a file named test.png in the AppIconSet folder, and it will look like this:


Note: If you get error messages or the script simply doesn’t work, it’s possible that you don’t have the necessary fonts installed. To find out which fonts are available to you, run the following command in Terminal:

convert -list font

If you don’t have the Times font, choose one from the list and use that instead.

OK, now that you’ve done that, you’ll put a beta ribbon on it. In Terminal type:

composite betaRibbon.png test.png test2.png

This takes betaRibbon.png and places it on top of test.png, and then saves the composited image to test2.png.

Open test2.png. Wait, It looks just like the original test.png!


So here’s what’s going on:

test.png is 120 x 120 pixels in size. However, betaRibbon.png is 1024×1014 pixels, so only the transparent parts of betaRibbon.png are applied to test.png, and the rest is cropped.

Don’t believe me? Try running the same command, but swap the places of betaRibbon.png and test.png:

composite test.png betaRibbon.png test2.png

You’ll now see a much larger image with the ribbon where it’s always been and the test.png image aligned at the top left:


For this to work correctly, you need to resize betaRibbon.png to be 120 x 120. This is very easy in ImageMagick – just type:

convert betaRibbon.png -resize 120x120 smallBetaRibbon.png

This command resizes betaRibbon.png to 120 x 120 pixels and saves the result as smallBetaRibbon.png.

Now, execute the following:

composite smallBetaRibbon.png test.png test2.png

Open test2.png, and you’ll see it now looks like it’s supposed to:


So that’s all of the ImageMagick functionality you’ll need for this tutorial, but know that it’s just the tip of the iceberg as far as what ImageMagick can do. Check out ImageMagick’s home page to learn more.


After all that image processing work, it’s time to cleanse your palate by working in some familiar territory.

Open Xcode, select File\New\Project…, choose the iOS\Application\Single View Application template, and click Next. Name the project Llama Trot, set the selected language to Swift, and set the device family to Universal. For simplicity, save the project to the desktop.

Your goal is to have Xcode and ImageMagick generate an appropriate icon every time you build the project, based on the selected build configuration.

Xcode has the ability to use run scripts to do things when your project is built. A run script is just a Unix script, like what you’ve already written, that executes every time you run your Xcode project.

Setting Up a Run Script

In your project, select the Llama Trot target, and navigate to Build Phases. Click the + icon and select New Run Script Phase from the popup menu:

Screen Shot 2015-05-10 at 10.53.12 PM

You’ll see the Run Script Phase added to your project’s phases.

Within that run script, the Shell parameter should be auto-set to bin/sh, which means that this script will execute in the bash Unix shell.

Underneath the shell parameter is a box for you to enter your script. Type the line below into that box:

echo "Hello World"

Your new build phase should now look like the following:

Screen Shot 2015-05-10 at 10.54.54 PM

Build and run. You should see absolutely nothing interesting. That’s because the script printed Hello World to your build logs while you weren’t looking.

Navigate to the Report Navigator-that’s the icon on the far right of Xcode’s Navigator Pane-and click on the most recent build report, as shown in the image below. In the wall of text describing all of the work that Xcode does for you when you build a project, you’ll see the text Hello World:


App Icons

Great, you’ve gotten a script to output Hello World, like you’ve probably done a million times by this point in your career as a developer. Now it’s time to modify the app icons.

Finding the App Icons From Your Script

Add all the app icons to your Images.xcassets asset catalog by navigating to Images.xcassets in Xcode and dragging each icon into it’s appropriate tile in the AppIcon image set:


You’ll also need to put debugRibbon.png and betaRibbon.png in your project’s root directory-the same folder where your project’s .xcodeproj file is.

Screen Shot 2015-05-10 at 10.47.33 PM

In order do anything with the icons, your script needs to know where they are. Replace your run script with the following code:

echo "${SRCROOT}"
  1. The first line prints the path to the folder that will contain the final icons after you run your project.
  2. The second line prints the folder path of where your project files are located.

This works by using some of Xcode’s many build settings’ variables.

Run your project and check the build report. You should see the folder path that describes the location of your project’s final products. Under that, you should see the folder path of your project directory:


Navigate to the first path in Finder and look at what’s in there; you’ll see all of the products of your app, including all of the app icons. This is where you’ll save the icons that ImageMagick modifies.

To see these icons, right-click on the Application icon in that folder, and then select Show Package Contents. For now they look perfectly normal!


Now navigate to the second folder path that you echoed out. This is just your normal project folder. So where are the app icons?

Go into the folder that has the same name as your project-in this case “Llama Trot”. In there you’ll see Images.xcassets. Open Images.xcasset and you’ll see yet another folder named AppIcon.appiconset.

The app icons are in that folder, and you’ll modify them with ImageMagick. Assuming you saved your project to the desktop and named it Llama Trot, the path to the icons is ~/Desktop/Llama\ Trot/Llama\ Trot/Images.xcassets/AppIcon.appiconset

You’re going to cheat a little bit to get the full path to the initial icons, so replace the last line of your script with:

echo $(find ${SRCROOT} -name "AppIcon60x60@2x.png")
  1. The first line temporarily sets the IFS-internal field separator-to the newline character. If you don’t do this, the second line will fail because the file name, Llama Trot, contains a space-try commenting out the first line if you’re curious about what happens.
  2. The second line in this command searches the ${SRCROOT} folder recursively for the file AppIcon60x60@2x.png.

By the way, it’s important that you type the line IFS=$'\n' precisely as it appears–no spaces on either side of the = sign.

Run the project, and you’ll see the full path to AppIcon60x60@2x echoed out:


Putting It All Together

The hard work is over. Now it’s time to put everything together and get your script to modify the app icons properly. You’ll start by only modifying AppIcon60x60@2x.png, and then work towards generalizing it for all of the app icons. This means you’ll need to test with a retina @2x iPhone simulator or device-so not the iPhone 6+.

By combining the techniques from ImageMagick and your previous script, you end up with the following script. Make sure to update your script accordingly:


BASE_IMAGE_PATH=$(find ${SRCROOT} -name "AppIcon60x60@2x.png") 

convert betaRibbon.png -resize 120x120 resizedBetaRibbon.png 

convert ${BASE_IMAGE_PATH} -fill white -font Times-Bold -pointsize 18 -gravity south -annotate 0 "Hello World" - | composite resizedBetaRibbon.png - ${TARGET_PATH}

Here’s a breakdown of what’s happening:

  1. The build will fail if you omit this line. Your terminal has a variable called PATH that stores a list of default script locations. Terminal looks there first for any commands that aren’t a part of Unix by default. This allows any command located in a directory in PATH to run without specifying the full location of the command. Xcode needs to share the same PATH variable as your terminal. This line adds /user/local/bin to the PATH variable where Homebrew installs its packages.
  2. The next two lines get the location of the app icons, as before, and then they save each path into variables TARGET_PATH and BASE_IMAGE_PATH respectively.
  3. This line sizes the beta ribbon down to the appropriate size
  4. The last line does two things at once. First, it annotates the initial app icon with the text Hello World. The script then pipes into the composite function — that’s what the | symbol is all about — and places the resized beta ribbon on top of it. ImageMagick does this by using a dash “-” instead of the file name. The script saves the result as the product app icon.

Note: App icon names are not arbitrary. The name of the app icons must be like AppIcon60x60@2x.png in the final product. Xcode uses this naming convention to determine which icon to use based on which device is in use.

Run the app and go to your device’s home screen to see the icons for your app; if you’re on the simulator, press Cmd + Shift + H to get to the home screen. You should see a modified icon:


The Rest of the Icons

Now that you’ve done quite a bit with one icon, it’s time to generalize the script to accomodate all your icons, so it will work with iPad icons, the iPhone 6+ icon etc.

To do this, you’ll make the icon modification code into a function that takes the icon’s name as an argument. Then execute that function for every icon that you have.

Modify the script so it looks like the following:


function generateIcon () {
  WIDTH=$(identify -format %w ${BASE_IMAGE_PATH})
  convert betaRibbon.png -resize $WIDTHx$WIDTH resizedRibbon.png
  convert ${BASE_IMAGE_PATH} -fill white -font Times-Bold -pointsize 18 -gravity south -annotate 0 "Hello World" - | composite resizedRibbon.png - ${TARGET_PATH}

generateIcon "AppIcon60x60@2x.png"
generateIcon "AppIcon60x60@3x.png"
generateIcon "AppIcon76x76~ipad.png"
generateIcon "AppIcon76x76@2x~ipad.png"

This puts the entire image processing code into a function called generateIcon(), and you pass the name of the icon to process as an argument. The script accesses this argument by using $1, and sets it to the variable BASE_IMAGE_PATH. ${BASE_IMAGE_PATH} then takes the place where AppIcon60x60@2x.png used to be.

You’ll also notice a new ImageMagick function, identify, and this function gets information about an image. In this case, you want to use the width that you obtain with the -format %w parameter on identify to determine how to resize betaRibbon.png.

Now, switch to an iPad or an iPhone 6+ simulator and run the project; you’ll see the modified app icons on the home screens.

You’ll see that on the new devices, the font size appears to be inconsistent. This is because the font size is expressed in pixels, and different device screens have different pixel densities.


There’s an easy fix for this. All you really want is the height of the text to be a certain proportion to the height of the entire icon.

Add the following line to your script immediately after where you set the WIDTH variable:

FONT_SIZE=$(echo "$WIDTH * .15" | bc -l)

This line is a little tricky, but what it does is set a FONT_SIZE variable to be one-fifth of the WIDTH variable. Since Unix arithmetic doesn’t support floating point arithmetic, you must use the bc program.

Short for basic calculator, bc can do floating point calculation. Since it’s a stand-alone program, the string $WIDTH * .15 needs to piped in for it to execute what you want.

Now, change last line of generateIcon() to use the value of FONT_SIZE instead of the hard-coded value of 18. The resulting script looks like this:


function generateIcon () {
  WIDTH=$(identify -format %w ${BASE_IMAGE_PATH})
  FONT_SIZE=$(echo "$WIDTH * .15" | bc -l)

  convert betaRibbon.png -resize $WIDTHx$WIDTH resizedRibbon.png
  convert ${BASE_IMAGE_PATH} -fill white -font Times-Bold -pointsize ${FONT_SIZE} -gravity south -annotate 0 "Hello World" - | composite resizedRibbon.png - ${TARGET_PATH}

generateIcon "AppIcon60x60@2x.png"
generateIcon "AppIcon60x60@3x.png"
generateIcon "AppIcon76x76~ipad.png"
generateIcon "AppIcon76x76@2x~ipad.png"

Run your project on various devices, and you’ll see that things look much better.


Goodbye World, Hello Build Number

As tempting as it’s to leave the Hello World text as an homage to the early days, you want to put the build number on these icons, and actually, it’s a pretty easy thing to do.

The build number can be found as the CFBundleVersion entry in the Info.plist file of your project.

So how are you going to get it into your script? As it turns out, your Mac came with a program to help you do this. It’s called PlistBuddy, and you’ll find it in /usr/libexec/.

Add the following line to the very top of your script:

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")

This line shows how to use PlistBuddy to get your build number. Now simply replace the “Hello World” part of your script with $buildNumber:

convert ${BASE_IMAGE_PATH} -fill white -font Times-Bold -pointsize ${FONT_SIZE} -gravity south -annotate 0 "$buildNumber" - | composite resizedRibbon.png - ${TARGET_PATH}

In the General tab of your target’s settings, change your Build number to 2015:


Now run the project. You’ll see an icon with the corresponding build number:


Build Configurations

Cool! The full loop is complete. You’ve’ overlaid a beta ribbon and the build number to the app icon and it runs every time you build your project.

But you don’t want a beta ribbon and build number all the time. What if you’ve got an alpha build? Or more likely, what if you’re releasing a version and have a release build? You definitely don’t want to put the build number on that.

That’s where Build Configurations come in.

In Xcode, go to your project’s configurations. You should see two configurations by default: Debug and Release.

Press the +, choose Duplicate Release and rename it to Beta. This creates a new build configuration setup that is exactly the same as the Release configuration.

Screen Shot 2015-05-10 at 7.50.48 PM

The Debug configuration will be the alpha/debug version, the Beta configuration will be the beta version, and the Release configuration will be the release version of the app.

Now all you need to do is pull the configuration into your script with the CONFIGURATION build settings variable, and add an if statement into your script determine to the current configuration. Update your script to the following:

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")
versionNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}")

function generateIcon () {
  echo $SRCROOT
  echo $(find ${SRCROOT} -name "AppIcon60x60@2x.png")
  WIDTH=$(identify -format %w ${BASE_IMAGE_PATH})
  FONT_SIZE=$(echo "$WIDTH * .15" | bc -l)
  echo "font size $FONT_SIZE"
  if [ "${CONFIGURATION}" == "Debug" ]; then
  convert debugRibbon.png -resize ${WIDTH}x${WIDTH} resizedRibbon.png
  convert ${BASE_IMAGE_PATH} -fill white -font Times-Bold -pointsize ${FONT_SIZE} -gravity south -annotate 0 "$buildNumber" - | composite resizedRibbon.png - ${TARGET_PATH}
  if [ "${CONFIGURATION}" == "Beta" ]; then
  convert betaRibbon.png -resize ${WIDTH}x${WIDTH} resizedRibbon.png
  convert ${BASE_IMAGE_PATH} -fill white -font Times-Boldr -pointsize ${FONT_SIZE} -gravity south -annotate 0 "$buildNumber" - | composite resizedRibbon.png - ${TARGET_PATH}

generateIcon "AppIcon60x60@2x.png"
generateIcon "AppIcon60x60@3x.png"
generateIcon "AppIcon76x76~ipad.png"
generateIcon "AppIcon76x76@2x~ipad.png"

Here’s what the changes enable:

  • If the configuration is Debug, the script uses a debugRibbon.png.
  • If the configuration is Beta, the script uses betaRibbon.png like before.
  • If the configuration is Release, the script won’t do anything because the app icon should remain unmodified.

To change the build configuration, Choose Product\Scheme\Edit Scheme… select Info and then choose the build configuration based on which action you’ll do e.g., Run, Archive, Profile, etc.

Screen Shot 2015-05-11 at 1.51.34 AM

Changing the build configuration for Run will reflect in your testing.

And that’s it! You have build numbers and ribbons for the debug and beta version of you app!



Where To Go From Here?

This tutorial was pretty heavy in scripting, so if you got through it all, congrats!

You can get the version number of your project with /usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString".

You might want to use that to provide the version number in addition to the build number on your icon.

There are numerous conveniences where Build Scripts can help you with all kinds of jobs. Here are some of my favorites:

  • Bump the build number every time an archive is made.
  • Automated git tagging
  • Generating seed databases before an app launch

Thanks for working through this tutorial today! You covered quite a bit and now know how to keep your various builds organized and clearly labeled for yourself and any testers you’re working with. And you also learned how to use Build Scripts and ImageMagick to resize and mark up images based on your own parameters. Talk about power!

Please feel free to share your questions, key learnings and any other ideas you have for using Build Scripts in the forums below.

Average Rating


Add a rating for this content

1 rating

More like this