Fat Cat Software

Preprocessing InfoPlist.strings files in Xcode

Xcode provides a handy feature called "Info.plist preprocessing" that allows you to specify placeholder values in your plist that are then automatically replaced by Xcode with values you specify in your build settings. The most common use of this is to place the current build's version number in one or more appropriate places in the Info.plist file. This lets you change these kinds of values in one place, rather than having to track down every usage in the Info.plist and replacing them manually.In addition to the Info.plist file itself, you can also have one or more localized InfoPlist.strings files included in your application bundle. There are some strings in the Info.plist (e.g. CFBundleGetInfoString) which are displayed directly to the user, and thus need to be localized. Putting these keys in InfoPlist.strings instead of directly into Info.plist allows you to have localized versions of these strings.However, Xcode's Info.plist preprocessing only covers the actual Info.plist file, and not the various InfoPlist.strings files you may have included in your application bundle. This means that if you have a version number or some such that you want to keep up to date in one of these strings, you have to resort to replacing them manually, which is both error prone and a pain in the ass. I was able to solve this problem for my own project, and thought I'd share the details here in case it came in useful to anyone (and in case I forgot how to set it up in a future project and needed some help from my past self :) )To get things working, I enlisted the help of a neat little tool called wincent-strings-util, courtesy of Wincent Colaiuta. It performs several tasks, but one of them is specifically geared towards doing this kind of substitution in a .strings file. In my setup, I simply include a copy of the tool in my project repository, so no installation is required and the build "just works" wherever you check out the project.To get the substitution to be done when doing a build in Xcode, I defined a new build rule in my target to use a custom script to handle Info.plist files. To do this:Choose "Edit Active Target" from the Project menu, click on the "Rules" tab, then click the "+" button down at the bottom of the window to add a new build rule.From the "Process:" pop-up menu, select "Source files with names matching:" and then "*/InfoPlist.strings" in the field next to the pop-up. When I first tried this, I entered simply "InfoPlist.strings", but that didn't work because the match is against the whole path of the file, and not just the filename, so the * is necessary to match any file named "InfoPlist.strings".From the "using:" pop-up menu, select "Custom script:", then enter the following script text:cd "${BUILT_PRODUCTS_DIR}" && ${SRCROOT}/DevFiles/wincent-strings-util --info "${INFOPLIST_PATH}" --strings ${INPUT_FILE_PATH} --output /tmp/${INPUT_FILE_NAME} --encode UTF-16LEWhere it says "With output files", click the "+" button and enter "/tmp/${INPUT_FILE_NAME}" for the output filenameYou can click here for a screenshot showing the completed build rule setup. A couple things to note:My major source of problems in getting this working was trying to deal with spaces in the various path names (my product name is "PlistEdit Pro"), and splicing together multiple build setting environment variables was not working well. That's the main reason the script does a "cd" into the build directory, then works from there. That's also the reason that I spit the output file into /tmp rather than somewhere inside the Xcode build folder, since trying to get it working there was more headache than it was worth.As I said, I keep a copy of wincent-strings-util inside my source repository. In my case, in lives in a subfolder named "DevFiles". You should of course change this path in the script to the location where you choose to store tho wincent-strings-util executableThe "With output files" section tells Xcode where to look for the result of the custom script. Xcode will then copy that result file into the appropriate location inside your built application bundle. If you do change where the --output argument of wincent-strings-util goes, make sure to change the output file to match.Finally, to actually do a substitution, simply put a key from your Info.plist into the InfoPlist.strings file, enclosed in chevron brackets («»). For example, I have the following entry in my InfoPlist.strings file:"CFBundleGetInfoString" = "PlistEdit Pro version «CFBundleShortVersionString», Copyright 2004-2009 Fat Cat Software.";This will replace the «CFBundleShortVersionString» placeholder with the value stored under CFBundleShortVersionString in the main Info.plist file. I have my Info.plist preprocessing already set up to include the version number of the build for CFBundleShortVersionString, so this will propagate that to the InfoPlist.strings files, and my version number will display correctly in the Get Info window for all localizations.