Archive for the ‘Nerdery’ Category

Make Xcode nag you about unfinished TODOs

Friday, January 8th, 2010

If you’re like me, you often make promises to yourself in the form of TODO comments in your code. For example:

// TODO: make sure file exists before opening!
fooBar = [[FooBar alloc] initWithFile:path];

This is a reasonable thing to do, because sometimes you just want to get something working right now and aren’t in the mood to write all the required error checking code. But, you also know that you cannot trust your soft human brain to remember to add the check later, so you write a comment to remind yourself to do it.

Xcode recognizes the TODO: keyword in your comments and helpfully adds items to the function popup menu so that you can quickly navigate to them. In addition to TODO:, Xcode will also recognize FIXME: (when you know the code is broken), ???: (when you don’t know what it does), and !!!: (when you wish you didn’t know).

That’s helpful when you’re editing a file, but what about a TODO tucked away in some dark corner of your source code that you haven’t visited in a while? You’re likely to forget about it, and how can you keep a promise you forgot that you made?

The answer, of course, is to have somebody nag you. Fortunately, there’s a way to have Xcode fill that role. All you have to do is add a simple Run Script Build Phase which turns them into Build Warnings.

Select Project > New Build Phase > New Run Script Build Phase from the menu bar. Then, copy and paste this into the script window:

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

What does it mean?

Line 1 defines the keywords we want to search for. If you want to exclude a keyword or include a different one, edit this line.

Line 2 uses the find command to generate a list of all files in your project directory (SRCROOT) having an .h or .m extension. If you want to search more files, you will need to edit this line.

Line 3 uses xargs to pass those file names along to egrep, which searches inside the files for lines containing one of the keywords. If any are found, it outputs the file name, line number, and the matching part of the line.

Line 4 uses Perl to format the lines as warnings.

The output of the script will look like this:

/Users/benzado/Projects/FooBart/Baz.m:42: warning: TODO: make sure file exists before opening!

Xcode will recognize lines in this format and treat them as first class build warnings. You can see them in the Build Results panel and, just like a warning from the compiler, a double click will open an editor window and take you directly to the offending line.

An Exercise For The Enterprising Reader: modify the script so that no warnings or errors are reported during Debug builds, but TODOs are flagged as errors in Release builds.

Draw your own Disclosure Indicator

Sunday, January 3rd, 2010

I’m writing Cocoa Touch code to draw a button which, when pressed, pushes a new view controller onto the stack. If I was working with a UITableView, I’d simply set the cell’s accessory to be a disclosure indicator (the little gray arrowhead) and call it a day.

But I’m not working with table cells, so even though the standard disclosure indicator is perfect for this situation, if I want one I’ll have to draw it myself.

In this situation I will usually take a screenshot of the real iPhone control, add the image file to my project, and then feel kind of guilty about it. I began to do this, but I realized that the disclosure indicator is really only two gray lines. Two lines! How hard can it be to just draw it in code?

It turns out that it’s not too hard, if you’re willing to spend some time experimenting with different numbers and seeing what looks right. Fortunately for you, I’ve saved you the trouble by posting the answer here:

// Draws a disclosure indicator such that the tip of the arrow is at (x,y)
void BRDrawDisclosureIndicator(CGContextRef ctxt, CGFloat x, CGFloat y) {
    static const CGFloat R = 4.5; // "radius" of the arrow head
    static const CGFloat W = 3; // line width
    CGContextSaveGState(ctxt);
    CGContextMoveToPoint(ctxt, x-R, y-R);
    CGContextAddLineToPoint(ctxt, x, y);
    CGContextAddLineToPoint(ctxt, x-R, y+R);
    CGContextSetLineCap(ctxt, kCGLineCapSquare);
    CGContextSetLineJoin(ctxt, kCGLineJoinMiter);
    CGContextSetLineWidth(ctxt, W);
    CGContextStrokePath(ctxt);
    CGContextRestoreGState(ctxt);
}

Before calling the function, you should set the stroke color to 50% gray if you’re drawing on a white background or white if the control is highlighted and you’re drawing on a blue background. Or, you can use whatever color you like.

To be honest, I only eyeballed the result, so it might not be a pixel-perfect reproduction of the real thing. However, I think my eyeballs are at least as good as most users’ eyeballs, so I will confidently declare this code Good Enough.

Nicer ways to view UNIX man pages

Sunday, November 29th, 2009

There is a wealth of information available in the UNIX manual pages, but it feels a little silly to be reading text out of a terminal window, especially since I paid so much money for all this fancy font rendering technology.

Here are some alternatives:

To view a man page in Xcode’s documentation window, simply select “Open man Page…” from the Help menu. For some reason, I hadn’t noticed that menu item until I read this hint on accessing it via AppleScript. Nicely formatted and references to other man pages are hyperlinked.

Bwana allows you to read man pages in any web browser, by registering itself as a protocol handler for man: URLs. Once installed, you can type man:perl in your browser’s address bar or open man:perl at a command prompt to read a manual page in your browser. Like Xcode, cross-references become hyperlinks, but it formats text using Courier. Source code is available, so I guess if I care enough I can do something about it.

Finally, a surprisingly short incantation will open any man page as a beautifully formatted document in Preview:

man -t perl | open -f -a /Applications/Preview.app

The -t option tells man to output PostScript, and the -f option tells open to put its input into a temporary file and pass that along to the specified application.

After reading that hint I set about writing a shell function so I could type manp perl to open Perl’s manual page in Preview. However, Preview’s PostScript to PDF conversion is kind of slow, so I wound up writing something slightly more sophisticated:

function manp {
    local M=`man -w $*`               # Get path of page source.
    if [ -z $M ]; then return; fi     # Quit if it doesn't exist.
    local N=`basename $M .gz`         # Extract the name of the file and
    local P=$TMPDIR/man.$N.pdf        # use it to create a PDF file name.
    if [ ! -e $P ]; then              # If the PDF file doesn't exist,
        echo Creating PDF for $N...
        man -t $1 | pstopdf -i -o $P  # generate it.
    fi
    open $P                           # Open the PDF version.
}

Then I went back and read the comments, which contain a dozen or so different versions of the same idea. But mine’s the best! Copy and paste it into the your .profile if you agree.