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. By .

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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:

PATH=${PATH}:/usr/local/bin
IFS=$'\n'

function generateIcon () {
  BASE_IMAGE_NAME=$1
    
  TARGET_PATH="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/${BASE_IMAGE_NAME}"
  BASE_IMAGE_PATH=$(find ${SRCROOT} -name ${BASE_IMAGE_NAME})
    
  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.

IconsBefore

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:

PATH=${PATH}:/usr/local/bin
IFS=$'\n'

function generateIcon () {
  BASE_IMAGE_NAME=$1
    
  TARGET_PATH="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/${BASE_IMAGE_NAME}"
  BASE_IMAGE_PATH=$(find ${SRCROOT} -name ${BASE_IMAGE_NAME})
    
  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.

IconsAfter

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:

BuildNumber

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

AppendedBuildNumber

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:

IFS=$'\n'
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}")
versionNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}")
PATH=${PATH}:/usr/local/bin

function generateIcon () {
  BASE_IMAGE_NAME=$1
    
  TARGET_PATH="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/${BASE_IMAGE_NAME}"
  echo $TARGET_PATH
  echo $SRCROOT
  echo $(find ${SRCROOT} -name "AppIcon60x60@2x.png")
  BASE_IMAGE_PATH=$(find ${SRCROOT} -name ${BASE_IMAGE_NAME})
  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}
  fi
    
  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}
  fi
}

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!

AppIcon60x60@3x

App2Beta