Xcode iPhone Project Tools

Useful rake tasks and scripts for iPhone development using Xcode. Some tasks rely on external software. Please read the installation instructions.

The scm related tasks assume that you are using git. You can just ignore those tasks and scripts if you are using a different scm.

Installation

  1. Install the iPhone Project Tools (ipt) gem

    sudo gem install juretta-ipt --source http://gems.github.com
    
  2. Adde the following lines to your Rakefile:

    require 'rubygems'
    gem 'juretta-ipt'
    load 'iphone_tools.rake'
    

Rake tasks

The Rakefile contains tasks that are valueable for iPhone development. It defines tasks to:

  • Do static code analysis using the LLVM/Clang Static Analyzer
  • Create a XML/HTML changelog using the git commit log.
  • Generate an HTML README file using an existing markdown, textile or RDoc formatted README.* file
  • List TODO|FIXME|IMPROVE marker.

In your Xcode project directory run: rake -T to show the available tasks:

    rake ipt:analyze          # Analyze code
    rake ipt:clean            # Clean build artifacts
    rake ipt:dist:info        # Info
    rake ipt:dist:show        # Show all bundle identifier
    [...]

Static code analysis

If you want to use the static code analysis task (highly recommended). You have to download the checker binary.

stefan@macbook-pro-2:~/temp$ curl -OL http://keeda.stanford.edu/~kremenek/checker/checker-107.tar.bz2
stefan@macbook-pro-2:~/temp$ tar xjf checker-107.tar.bz2 -C /Users/stefan/dev/iphone/clang

Add the path to the extracted directory to your PATH environment variable. E.g. in ~/.bash_profile

export CLANG=/Users/stefan/dev/iphone/clang/checker-107
export PATH=$PATH:$CLANG

The LLVM/Clang static analyzer is a standalone tool that find bugs in C and Objective-C programs.

Run 'rake ipt:analyze' to run the clang checker. The output of the analyzer is a set of html files containing bug descriptions and source code location. If bugs are found the tool will open a web browser with the bug report. If no bugs are found (which doesn't mean there aren't any!) the tool will exit without further action.

Please visit the LLVM/Clang homepage to learn more about the "Clang checker". You should probably read the Recommended Usage Guidelines.

Generate HTML-Readme files

If your Xcode project directory contains a README.md,markdown,text,textile,rdoc file you can run the

rake ipt:readme

task to generate HTML Readme files.

Supported formats:

  • Markdown: README.md, README.markdown
  • Textile: README.text, README.textile
  • Ruby RDoc: README.rdoc

List (TODO|FIXME) Markers in your source code files

Suppose you added a TODO marker in one of your classes.

Run

rake ipt:todo

to list TODO|FIXME|IMPROVE markers in the source code.

Classes/DomainSearch.m
    95:  // TODO: Do we need a custom NSAutoreleasePool??

Easily create AdHoc distribution builds.

The ipt:adhoc:* tasks make it very easy to create proper adhoc builds that can be distributed as tar.gz archive files. The archive contains the iPhone Application, the AdHoc provisioning profile and a changelog .txt file that contains the latest changes.

The archive file can be easily distributed and allows beta tester/clients/developers to install a snapshot or development release.

I usually use the following directory laout when working on iPhone projects:

Main project directory:
    ./releases          This is where the AdHoc releases end up
        1.0.1.tgz
        1.0.2.tgz
    ./xcode-project     The Xcode project.
        Classes
        build
        Rakefile
        ...

The AdHoc builds are based on git tags representing the current version of the software.

rake ipt:adhoc:release 

rake ipt:adhoc:snapshot

Xcode and git

Xcode does not directly support git as a SCM but git can still be used to manage the source code of an Xcode project.

Run the rake ipt:git:changelog task to generate an HTML and XMl changelog file in the 'doc' subdirectory in your Xcode project directory. The updated changelog files will be added and commited to your local git repository automatically.

Xcode specific .gitignore

The

rake ipt:git:ignore

target either creates a new .gitignore file in your project directory or adds the following lines to your existing .gitignore file (but only if the are not already in there):

build
*.mode1v3
*.mode2v3
*.nib
.DS_Store
*.swp
*.pbxuser
*.perspective
*.perspectivev3

git now will ignore Xcode build artifacts and user specific settings.

Xcode git build number script

Inspired by: http://www.cimgf.com/2008/04/13/git-and-xcode-a-git-build-number-script/

CFBundleVersion

This key specifies the build version number of the bundle, which identifies an iteration (released or unreleased) of the bundle. This is a monotonically increased string, comprised of one or more period-separated integers. This key is not localizable.

The problem with most "Xcode git versioning scripts" is that they use the git commit id as a CFBundleVersion identifier. The Apple App-Store requires an bundle identifier as describe above (e.g. 1.0 or 2.3.1).

I use the following workflow to set version numbers for my Xcode project:

  1. tag the current release snapshot using

    git tag -a -m "Tagging current version as 1.0.1" 1.0.1
    

    You can list all you tags using:

    git tag
    

    To check which tag is going to be used as you version numer use:

    git describe --tags --always
    
  2. run Xcode build with the script that follows (which basically uses 'git describe --tags --always' to determine the most recent tag that is reachable from a commit)

  3. check CFBundleVersion value in all build artifacts using the rake task 'dist:show'

    rake ipt:dist:show 
    

In Xcode add a new script build phase: Targets > Add > New Build Phase > New Run Script Build Phase

Messages written to $stdout will end up in the XCode build log (CMD + Shift + B).

Use /path/to/ruby as the 'Shell' parameter. This is usually /usr/bin/ruby but might be different if you have a custom ruby installation (mine is /opt/local/bin/ruby). Run 'which ruby' in your terminal to determine the correct path to your Ruby executable.

require 'rubygems'
begin
  require 'Plist'
rescue LoadError => e
  puts "You need to install the 'Plist' gem: [sudo] gem install plist"
  exit 1
end

raise "Must be run from Xcode" unless ENV['XCODE_VERSION_ACTUAL']

GIT="/opt/local/bin/git"
PLIST_FILE = File.join(ENV['BUILT_PRODUCTS_DIR'], ENV['INFOPLIST_PATH'])

if File.file?(PLIST_FILE)
  pl = Plist::parse_xml(PLIST_FILE)
  if pl
    pl["CFBundleVersion"] = `#{GIT} describe --tags --always`.to_s
    pl.save_plist(PLIST_FILE)
  end
end

Useful Tools

rbiphonetest

Have a look at rbiphonetest if you want to easily test your Foundation classes. The rbiphonetest git repository lives here: http://github.com/drnic/rbiphonetest/tree/master