i’m so full of ideas

Archive for June, 2009

App Store rite of passage

June 25, 2009 5:21 am

I just got my first iPhone app into the store! iTunes link This can be a traumatic experience for many developers, and we all learn more about what the process is like through blogs, so here’s my own war story.

I submitted my app on June 14, 2009. It went live on the store on June 24. Let’s call that ten days. Better than some, worse than others. Plenty acceptable for me.

You’ve no doubt read about the horrors of trying to get your app properly signed for deployment on hardware devices and for distribution through Apple’s store. It certainly happened to me as well. I’m not going to go into all the details, because I followed so many dead ends that I’m not sure what I did anymore. I’ll mention the main thing that bit me hard, though. My app’s name has a space in it. When Xcode expanded the ${PRODUCT_NAME} variable into my app’s identifier, it replaced that space with an underscore. This worked fine for ad hoc builds, but the app store flatly refused to accept it. The error message I got didn’t tell me what the real problem was. After much googling and reading other people’s anguished cries, I solved it by removing the underscore. It looks like current versions of Xcode expand spaces to dashes, which is hopefully acceptable for distribution.

I was most worried about HIG violations. I did a lot of things in my app that are not very, um, “Apple-like.” My game has extensive settings, but rather than creating a settings bundle, I put them into a tab on my tab bar. I spent quite a bit of time writing a view controller that uses a UIWebView to display help pages, complete with back and forward buttons for navigation. None of Apple’s apps seem to have any help text anywhere. Apple wants you to use your Default.png to make it look like your app loaded faster than it did, but to me that sounds like lying, so my version simply has the word “Loading…” on it. I created an “about” page. Perhaps worst of all, I created a view that displays the app’s memory usage statistics, which will probably confuse the hell out of most people. I very nearly took that out of the final app. But Apple didn’t give me any trouble about any of that stuff. My app sailed through the approval process with nary a peep.

Based on other people’s stories, here’s some problem areas I might have avoided. I made sure that my artist used more or less the same content for the app’s 57×57 and 512×512 icons. I read somewhere that Apple doesn’t like to see version numbers below 1.0.0, so that’s what I used for my app, although most of my projects seem to languish in the 0-dot-something range. I didn’t use any of Apple’s tab bar graphics, because they all have predefined meanings that don’t match what my tabs are for. I didn’t use any undocumented API calls, although I was sorely tempted a few times. I guess that was enough to keep me out of trouble, in this particular case.

printf() sucks

June 23, 2009 1:44 pm

I am a big fan of printf()-style debugging. It helps you get an overview of a problem that traditional debuggers are not so good at. So it’s a bit unexpected that I do not like printf() itself.

Why printf() sucks

printf() is optimized for a weird corner case that you almost never need. You’ll no doubt recognize this as standard usage:

  printf("Hello, World!\n")

Notice that newline character at the end. It’s a pain to type. As best as I can tell, it exists so you can do this:

printf("Starting long-running operation ..."); // long-running operation goes here printf(" finished.\n");

… which causes the output from both printf() calls to be printed on the same line. Swell. How many times have you needed to do that? I first started programming in C in the late eighties, and my lifetime total so far is zero. I typed thousands of unnecessary newline characters before I finally wised up and wrote a replacement.

Format strings are error-prone

The most likely problem you’ll have with printf() and functions like it is a mismatch between the format string and the variables presented to it. For example:

  printf("Two strings: %s %s\n", "text");

The format string calls for two strings to be printed, but you’ve only provided one. The call to printf() might work fine, crash, or print weird results, depending on what happens to be lying around on the stack. If your compiler is GCC — and it probably is, if you’re programming for any UNIX variant, including Mac OS X — there is a good workaround. Here’s my definition for echo(), my printf() replacement:

  void echo(const char* tfmt, ...) __attribute__((format(printf, 1, 2)));

That weird __attribute__ business is a GCC-ism that means “this function works like printf(), and here’s the argument numbers to use for the format string and the variable args, respectively.”

This feature doesn’t work unless you specifically enable the proper GCC warning. If you’re using makefiles or the command line, pass -Wformat to the compiler. If you’re using Xcode, bring up the project information window. In the “Build” tab, there’s a section called “GCC 4.0 – Warnings.” The warning you want is labeled “Typecheck Calls to printf/scanf,” which should be enabled. Once you do that, then when you write code like this:

  echo("bad format: %s %s", "text");

The compiler will give you this warning:

  warning: too few arguments for format

… which saves you from the undefined behavior your program was about to be subjected to.

(C++ introduced cout, which is a printf() replacement. It handily works around the format string issue discussed here. I’ve always felt that cout introduces more problems than it solves, so I personally avoid it.)

Functions you can use

The sample code that follows includes three functions you might want to use in your own programs.

echo() — works exactly the same as printf(), except it doesn’t require a newline at the end of its format string.

sfmt() — works exactly the same as echo(), except it puts the formatted contents into a std::string object, rather than printing to stdout.

fmtArg() — useful if you want to build your own printf()-like function similar to echo() or sfmt(). Have a look at how echo() uses it, which should be enough for you to get started.

Example code

First the header file, printf_sucks.h:

// printf_sucks.h -- printf() alternative // by allen brunson  june 18 2009 #ifndef PRINTF_SUCKS_H #define PRINTF_SUCKS_H // sfmt() and support functions std::string fmtArg(const char* tfmt, va_list args); std::string fmtArgLarge(int32_t byteCount, const char* tfmt, va_list args); std::string sfmt(const char* tfmt, ...) __attribute__((format(printf, 1, 2)));   // echo(), a better printf() void echo(const char* tfmt, ...) __attribute__((format(printf, 1, 2))); #endif  // PRINTF_SUCKS_H

Now the source file, printf_sucks.cpp:

// printf_sucks.cpp -- printf() alternative // by allen brunson  june 18 2009 #include <assert.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string> #include "printf_sucks.h" void echo(const char* tfmt, ...) {     va_list      args = NULL;     std::string  text;         va_start(args, tfmt);     text = fmtArg(tfmt, args);     va_end(args);         puts(text.c_str()); } std::string fmtArg(const char* tfmt, va_list args) {     static const int32_t  kBufferSize = 2 * 1024;         // the extra four bytes are to guard against buffer overruns     char     cbuf[kBufferSize + 4];     int32_t  size = 0;     assert(tfmt && tfmt[0]);     size = vsnprintf(cbuf, kBufferSize, tfmt, args);         if (size < kBufferSize)     {         return std::string(cbuf);     }     else     {         return fmtArgLarge(size, tfmt, args);     } } // called when fmtArg() didn't have a big enough buffer std::string fmtArgLarge(int32_t byteCount, const char* tfmt, va_list args) {     char*        cbuf = NULL;     int32_t      clen = byteCount + 10;     int32_t      size = 0;     std::string  text;         cbuf = static_cast<char*>(malloc(clen + 4));     if (!cbuf) return "";         size = vsnprintf(cbuf, clen, tfmt, args);     assert(size < clen);         text.assign(cbuf);         free(cbuf);     cbuf = NULL;         return text; } int main(int argc, const char** argv) {     echo("hello from echo: %s %d", "text", 2);     return 0; } // works like echo(), but puts the formatted contents into a std::string std::string sfmt(const char* tfmt, ...) {     va_list      args = NULL;     std::string  text;     assert(tfmt && tfmt[0]);     va_start(args, tfmt);     text = fmtArg(tfmt, args);     va_end(args);         return text; }

Drawing NSStrings in unusual rotations

June 1, 2009 8:10 pm

If you’re used to writing apps for desktop computers, the iPhone’s screen can seem awfully small. You must make creative use of every pixel available to you. One way to do that is to draw some text vertically, rather than horizontally, which I decided to do for an iPhone app I’m working on right now.

For drawing normal horizontal text, UIKit has a category called UIStringDrawing that provides convenient methods for drawing the contents of an NSString to the current graphics context, like drawAtPoint:withFont: and drawInRect:withFont:. Sadly, UIKit does not provide methods for drawing text in any other orientation except standard horizontal. I will now present an NSString category you can add to your own iPhone projects that allows you to draw vertical text from bottom to top, top to bottom, or horizontal text upside down.

My category supports the same three UITextAlignment options that UIKit provides for left, right, or centered text alignment. If using my drawInRect:... method to draw a string that’s too wide to fit within the rectangle supplied, the right end of the string will be truncated to fit, with an ellipsis character added.

UIKit does a better job of handling long strings than my code does. UIKit can wrap text to two or more lines, for example, which I did not try to emulate. The UIKit methods also allow you to provide a UILineBreakMode enum value, which gives you fine-grained control over how strings are truncated. I personally don’t need that much flexibility, so my category has no such support.

Finally, there is one last limitation of my category that might be a deal-breaker for you. The standard UIKit string-drawing functions can be used to display any Unicode character, so long as the font you’re using has a glyph for it. My category is limited to the roughly 255 characters present in the MacRoman character set. That means my code is good for displaying text in English and most European languages, like French and German, but it is completely unsuitable for text in, say, Chinese.

I’m aware that this is an outrageous limitation. You’d be hard-pressed to find a programmer who is more gung ho about Unicode than I am. I struggled mightily for several days trying to find a way around this. Sadly, this is due to the way that the underlying CoreGraphics drawing routines work, and there is no good way around it that I can see. I’ll cover this in more detail later, in case you’re interested.

Using the WBTextDrawing category

I’ve included a sample Xcode project that demonstrates the use of my WBTextDrawing category. Download the project by clicking here.

I’ve provided a screen-shot, but it isn’t much to look at. There are five strings displayed onscreen. The four strings at the edges of the view are displayed with my own WBTextDrawing category, drawn in all four available orientations. The string in the center is drawn with one of UIKit’s own drawInRect:... methods. The lines in red show the bounding rects used to draw the five strings. The four strings around the edges all have an associated green pixel, which illustrates the draw point used to draw that particular string. Naturally you wouldn’t draw the green and red bits in a real app. I added them to this demo so you’ll have a better idea of what’s going on.

Almost all the code in the project is boilerplate that can be safely ignored. To add my string-drawing category to your own program, copy WBTextDrawing.mm and WBTextDrawing.h out of this project and into your own. All other source files presented here are for demonstration purposes only.

You will note that WBTextDrawing.mm ends with an mm extension, rather than the usual m. That’s because this source file must be compiled as Objective-C++, due to the fact that it contains a small amount of C++. I know many Objective-C programmers have a strong aversion to C++, and believe me, I understand! But the underlying CoreGraphics method that’s used to draw rotated strings insists on being given a const char*. It does not work with NSString objects directly. So I chose to convert NSString objects to std::string objects just before drawing them. Yes, I could have accomplished this without leaving the confines of Objective-C, but std::string seems to me like the best tool for the job.

Specifying text drawing locations

UIKit’s UIStringDrawing category contains several drawAtPoint:... methods for drawing NSString objects. The point you pass to these methods is the far left end of the font’s baseline, as illustrated by the green point in the first figure. For the sake of compatibility, I chose to use this same convention for the drawAtPoint:... method in my own WBTextDrawing category. No matter what drawing orientation you’re using, the specified draw point is always the far left end of the font’s baseline, relative to the string being drawn. This is also the way the low-level CoreGraphics drawing routines work, conveniently enough. See the second figure for what this looks like when drawing a bottom-to-top vertical string.

UIStringDrawing also has several drawInRect:... methods. In this case, the rectangle supplied is the entire area allotted for drawing the string, illustrated by the red rectangles in figure one and figure two. Again, my own WBTextDrawing category does the exact same thing, for compatibility’s sake.

Text drawn is limited to MacRoman

This is without a doubt the worst limitation of my WBTextDrawing category. I see no good way around it, however.

Whatever method you use for drawing rotated text, you must accomplish two goals: 1) Apply a given font to the current drawing context, and 2) Draw the NSString supplied by the caller. The easiest way I can see to apply a font is to use CGContextSelectFont(). That method gives you two encoding options: kCGEncodingMacRoman, which uses the MacRoman encoding, or kCGEncodingFontSpecific, which means you must provide your own character-to-glyph translations, as far as I can tell. Ahem. UIKit can’t do this by itself, apparently, but the expectation is that us lowly app programmers should be able to do it? As if. So the only real option here is to use the MacRoman encoding, then use CGContextShowText() to display the text. This is the way my category works. Anything outside the MacRoman character set displays as gibberish.

There’s another method you can use to apply a font to the current context: CGContextSetFont(). This method doesn’t take any kind of encoding parameter at all, so it would appear to be immune from the problems presented by CGContextSelectFont(). Alas, once you’ve called that method, CGContextShowText() doesn’t work anymore. The CoreGraphics docs say you should instead call CGContextShowGlyphsAtPoint(). That function expects its caller to supply an array of glyphs, not characters. Which implies that you’ve got some method up your sleeve that will convert characters to glyphs for a given encoding. I don’t know about you, but I don’t have any such method lying around. So I’m stuck with boring old MacRoman.

This seems like an awfully strange limitation to build into the low-level CoreGraphics text drawing routines. If you know of any way around it, please tell me what it is, so I can update my text-drawing category appropriately.