Matthew Morey

I'm an engineer, developer, author, hacker, creator, tinkerer, traveler, snowboarder, surfer, and husband.

I create iOS apps professionally and independently.

Improved Xcode Build Phases

12 February 2014

I received some great feedback on my Xcode Build Phases post from the other day. Turns out there are some issues with the code complexity check:

  • File paths with spaces are not checked
  • CocoaPods (other people's code) are checked inadvertently
  • Duplicate warnings (exact same messages) are incorrectly treated as only one warning

Here is the improved version that fixes all of the reported issues:

find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -and \( -path "${SRCROOT}/Pods/*" -prune -o -print0 \) | xargs -0 wc -l | awk '$1 > 400 && $2 != "total" {for(i=2;i<NF;i++){printf "%s%s", $i, " "} print $NF ":1: warning: File more than 400 lines (" $1 "), consider refactoring." }'

Special thanks to Steven Hepting, Vadim Shpakovski, Adam Iredale, Eric Roller and Yuichi Fujiki for doing the actual work of finding and fixing these issues.

Here is the improved TODOs and FIXMEs script:

KEYWORDS="TODO|FIXME|\?\?\?:|\!\!\!:"
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -and \( -path "${SRCROOT}/Pods/*" -prune -o -print0 \) | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | perl -p -e "s/($KEYWORDS)/ warning: \$1/"

And for completeness, here is the less useful total lines of code script:

echo "Total lines of code:"
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -and \( -path "${SRCROOT}/Pods/*" -prune -o -print0 \) | xargs -0 cat | wc -l

There is still an issue with these build scripts, they are only checking source files that are located in the ${SRCROOT} directory. Oftentimes Xcode projects include files stored outside of ${SRCROOT}. Instead of using find we could use Xcodeproj to get a list of all referenced files regardless of their location on disk. Something like this:

$ xcodeproj show SampleProject.xcodeproj --format tree_hash |egrep '^\s+path: *'
path: SampleProject
path: file with space
path: AppDelegate.h
path: AppDelegate.m
path: Images.xcassets
...
path: System/Library/Frameworks/UIKit.framework
path: System/Library/Frameworks/Foundation.framework
path: libPods.a
path: en.lproj/InfoPlist.strings
path: file with space
path: Images.xcassets

But this would require you to have Xcodeproj installed, whereas find comes standard on all Macs.

If you go with the Xcodeproj method I would love to hear about it as I still can't make up my mind.