I just got work from our editor that iPhone SDK Development is “flying off shelves” and they need to rush to reprint. That’s a problem we’d like to have, of course!

Anyways, I’m taking a day off Next Exit to tend to small errata that can be fixed without major disruption… nothing that would take copy-editing or serious re-layout. There aren’t that many errata, and a few of them are my own (40984, for example), so it’s nice to have a chance to do a quick fix-up. There’s a little dust here and there where a Leopard screenshot already looks dated, but it’s nothing that should alarm anyone too badly. When Apple announces the inevitable iPhone SDK 4.0, then we’ll start sweating.

I haven’t had a lot of people asking about a Kindle version, so maybe that means the message has gotten out: if you buy the eBook and paper bundle directly from the Prags’ website, you get access to a Kindle-compatible mobi version, along with PDF, and a epub for use with the lovely Stanza e-book reader for iPhone. Daniel also informed me that if you’ve bought the hard-copy elsewhere, you can still upgrade to the e-bundle if you want an electronic copy, by registering it on your Prags bookshelf.

I do feel like my coding style has changed a little between writing a bigger app (Next Exit is about 7,000 LOC), and wonder how that would translate to a new edition or another book. My guess is that you’d see a lot more #defines for starters. I’ve also adopted the practice of moving the dealloc method to the top of the file — right after whichever form of init... is used — to make memory management more prominent and remind me to release those instance variables.

Two months ago, in Bringing Your Own Maps, I went off on an atypical excursion into the realms of location-based applications and how an iPhone developer would need to license data from providers to develop apps that provide turn-by-turn directions or other routing and location-based search functionality.

That was your hint that I was up to something.

Today, spurred by the AppsFire App Star awards and its requirement of a public YouTube demo, I’m revealing the project I’ve been working on for the past two months: Next Exit.

As I’ve been blurbing it:

Next Exit is the safe, sane way to find gas, food, and lodging along US highways, with a no-fuss, one-thumb interface

Allow me to explain further:

Why Do I Need a Special-Purpose Map App?

To pin down why I wrote this app, I’ll go back to a Summer trip to California with the family. We were driving back from Disneyland to San Diego and needed to get something for the insanely picky kids to eat. As it turns out, California doesn’t have those blue “services at the next exit” signs that are common elsewhere in the country (well, in every state between Michigan and Florida, at least). I-5 also had no billboards in this stretch. So, short of actually managing to see the elusive Taco Bell itself before passing the exit, there was no practical way to figure out where to get off.

So, yeah, I did the obvious thing and searched the Maps application for “Taco Bell”. While driving. Not smart.

Screenshot 2009.11.29 14.25.05

This sucks for a couple of reasons… the most obvious being the driver distraction and the vastly increased likelihood you’ll crash into someone or something while fussing with the phone. But even if you do manage to send off a search, the results are sub-optimal: it will search where you are, not where you’re going, meaning it’s just as likely that you’ll get results five miles off your current route, or even behind you, as it is likely to find results that you can actually use.

Most people on long freeway drives want services that are right there on the highway. This means a search needs to be a lot smarter:

  • Figure out what road the user is on and what way he or she is going
  • Figure out where that road goes
  • Find exits along that road
  • Find services of certain types within a certain distance of those exits

Next Exit is the app that provides that kind of search.

Let’s Watch the Video

At this point, let me point you to the video demo that I prepared for the App Star contest. They wanted something around 30 seconds, which I submitted as the short version, but this longer version is still under a minute and shows off more stuff:

Gee, That Doesn’t Look So Hard

This is an app that looks simple but is actually quite complex underneath. If you read the original “Bringing Your Own Maps” post, you’ll recall that the iPhone OS’ “Map Kit” provides visuals for maps, but doesn’t actually have any location data behind it. A diagonal line marked “Market St.” is just that – a bunch of pixels, and nothing more. To have any concept of streets, you need to go to third-party map services. On the iPhone, this is compounded by the fact that your code needs to be in C/Obj-C, while most of the mapping APIs are written for the popular server-side scripting languages, or JavaScript (which speaks to the larger point that the mapping companies see their developer audience as web developers, not embedded or desktop developers). The only thing that’s really practical on the iPhone is a webservice or other network-oriented API that can be called from Cocoa Touch’s networking classes. The downside: lots of XML parsing on the receiving end.

Then there’s an even deeper question of how you even solve this problem. The mapping APIs are largely written from the point of view of “given a starting point and a destination, find a route.” But the question posed by this app is “given a starting point and a direction, find potential destinations.” My initial version searched ahead for exits, drawing lines between the furthest ones to account for turns in the road, but could get thrown off when the current highway meets another, as the other highway’s exits onto the current highway ended up in the search results and weren’t practical to remove. In the end, I developed a complex but more reliable system of finding road segments for the current freeway, arranging them to create a path, and then searching this path for exits. I’ll be writing more about this geo-logic in future updates.

Oh, and remember: I don’t have the luxury of doing these searches on a local database. This is all back-and-forth with MapQuest’s web server, swapping and parsing XML.

MapQuest? Really?

The location data is provided by MapQuest, under a commercial license. Why them? It helps that they replied to my initial request for licensing terms (unlike some of the other companies in this space). But perhaps more importantly, MapQuest’s entire API is accessible via their web-based XML protocol, which is slowly being supplanted by a set of equally powerful web services. By comparison, a lot of the good stuff in Google Maps is only practical with JavaScript — they have an HTTP API, but it has big holes — which perhaps suits their web-centric view of the world, but doesn’t help the embedded developer.

Also, MapQuest seems eager to work with developers and to understand the iPhone App Store market. They brought an engineer to a sales call with me, and he helped me figure out the find-and-arrange-segments logic that cured the early prototypes’ tendency to turn off onto unrelated interstates. So far, I’m really liking working with them.

So When Does It Go On Sale?

I have one more major task to account for: in-app purchase. Since the use of the MapQuest service will create an ongoing cost for however long copies of the app remain in use, a free or one-time payment model is not going to work. A subscription model is more appropriate: pay as you go, stop paying if you stop using it.

The elaborate, turn-by-turn, singing-and-dancing apps like Tom Tom and Navigon are going for $100. Next Exit does a lot less – by design – and therefore should cost less. So I’m keeping it impulse-worthy:

  • $1.99 for the app and three months of service
  • $4.99 for each 12 months of service thereafter

Folks, that’s less than the cost of an upsized value meal… and with Next Exit, you’ll be able to find the road-side restaurants with the value meals you like, not the ones you’ll just settle for!

Anyways, with hopes of finishing in-app purchase (and an audit-trail server on my end… groan), finalizing things with MapQuest, and getting things through the App Store review process in the next few weeks, I’ve got a fighting chance of getting this out before people hit the roads for the holiday travel season. Or, if it comes out after Christmas, there’ll just be that many more new iPhones in play, looking for useful apps.

More, much more, to follow. For now, fingers are crossed that the Apps Star jury will like what they see. The prize in their contest is free publicity… exactly what a new app needs!

Author Peter Cooper posted a blog about his experience publishing Beginning Ruby for APress, a blog that got extraordinary traffic after being featured on Slashdot with a misleading summary. Much of the piece concerns itself with royalties, how they’re calculated, and how they’re paid. In his conclusion, he advocates writing for the Pragmatic Programmers which offers a 50%-of-profits royalty rate.

Tim O’Reilly himself saw fit to counter that advice, claiming that the Prags’ royalty isn’t what it seems, and to imply that a bigger publisher would move more books, meaning that a smaller royalty on bigger sales would cancel out the Prags’ advantage.

About a week later, the Prags’ Dave Thomas posted a blog of his own, spelling out the specifics of the Prags’ royalties.

I commented on Dave’s blog:

Having written one book with the Prags and two elsewhere, my mental taxonomy is now “writing for Prags” versus “writing for free”. Yes, between 10% royalty * coauthors * Amazon discounts * shrinking computer book market, it really is that bad.

But I have more to say, which is why I’m blogging now.

In my experience, and my understanding of the current nature of the computer book market, O’Reilly’s claims that its size give it an advantage in moving more books is probably true to a limited degree, but not enough to make up what is effectively a four-to-five-fold difference in royalty rates. With computer book sections shrinking year after year in brick-and-mortar bookstores, a huge majority of computer books are purchased online from Amazon and its lesser rivals (or just stolen, but that’s another story), which obviates the advantages a bigger publisher like O’Reilly would have in getting its product into more stores.

I wrote one book for O’Reilly and co-wrote another, both released in 2005. QuickTime for Java: A Developer’s Notebook is clearly a niche title (and the topic API is now deprecated), and if Swing Hacks is somewhat more noticable, it’s only because the Java development community is so large. I suspect only one in every 25 to 50 Java developers does Desktop Java, but over 5 million developers, that’s still enough to be interesting. Over their respective lifetimes, the QTJ book sold about 2,000 copies (bad), and Swing Hacks sold 10,000 (good).

The iPhone SDK Development book that I co-wrote for the Prags nearly outsold both of them, combined, just in pre-release beta sales. Granted, the topic is clearly more in demand, but still, enough people found their way to the Prags’ site to buy the beta that before a single final copy had the shelves. I’ve already outearned four years of royalties on the two O’Reilly books several times over. About the same number of copies, but much higher royalties. It’s that simple.

Aside: one factor that complicates comparing apples to oranges: since QTJ:ADN never outearned its advance, the royalties on I earned SH beyond its advance were applied to my royalty debt on QTJ until that was paid-up. I’m not sure if this is standard practice.

One other factor that I think is even more interesting is that the Prags presumably can offer a higher royalty because their overhead is very low. O’Reilly has a lovely campus in business-unfriendly California; I’m not sure the highly-distributed Prags even have a formal office. The low overhead is presumably what allows the Prags to offer such a high rate, but moreover, they’re able to take a chance on niche-ier topics. O’Reilly’s upcoming Cocoa and Objective-C: Up and Running is, by my count, their fourth introductory Cocoa programming book (following the awful ADC-authored Learning Cocoa, James Duncan Davidson’s rewrite of it, and Mike Beam’s Cocoa in a Nutshell). Apparently the broad Mac programming market is big enough to be interesting to them, but not any smaller part of it. If you want a book on Core Animation or Xcode, you pretty much have to look to other publishers (notable exception: the Bonjour/Zeroconf book).

To me, that’s a bigger deal, because your royalties are zero if a publisher won’t even put your title out there. Right now, the iPhone market has ample introductory titles (and Mac is almost there, once titles are updated for Snow Leopard or Daniel Steinberg finishes his overhauled-for-SL Cocoa book), and the next step is to get deeper into topics that are too large or too difficult to cover in introductory books. But almost by definition, these titles carve up the market for the introductory book, and only publishers who can make money off niches can produce such titles. Pretty safe to say that if we see a book on, say, Core Audio, or advanced use of the Xcode toolset (IB, Instruments, etc.), it’s going to be from one of these smaller publishers.

Like everyone else, I’ve tired of Obj-C’s dance of redundancy, specifically having to declare interface variables for properties if you want to run in the simulator (or the “old” Obj-C runtime on Mac). To speed things up, I create the ivars first, then copy-and-paste outside the @interface block to set up the properties. Which still sucks, because I have to prepend every line with the @property declaration.

9 times out of 10, the property is for a UI outlet, so I’m always setting it up as @property (nonatomic, retain) IBOutlet IvarType *ivarName

To speed things up, I finally figured out how to set up an Xcode text macro for this:


{
    Identifier = objc.property-iboutlet;
    BasedOn = objc;
    IsMenuItem = YES;
    Name = "IBOutlet @property";
    TextString = "@property (nonatomic, retain) IBOutlet ";
    CompletionPrefix = "@property";
    OnlyAtBOL = YES;
    IncludeContexts = ( "xcode.lang.objc.block" );
},

Then I used the Keyboard system pref to add an Xcode-only keyboard shortcut to the “IBOutlet @property” menu item (right now it’s cmd-option-I… we’ll see if that sticks).

And all this makes setting up my properties suck just a little bit less.

Late in the development of iPhone SDK Programming, I added a section to the networking chapter on web services, probably the top reader request. The focus of the section was on using NSXMLParser to parse a response from a web service received over the network, in this case the Twitter public timeline.

NSXMLParser is an event-driven parser: it calls back to a delegate as it encounters the beginning or end of each element, text, comment, etc. In the final book, we use a very simplistic delegate to pick off just the elements we care about, ignoring the rest. We went with this approach because an earlier beta of the book adopted the “parse the whole tree” approach suggested by Apple’s Introduction to Event-Driven XML Programming Guide for Cocoa, and the feedback from both editor and readers was that it was too hard and too much work for the sample problem.

And it was, despite one truly nifty technique that Apple provides you: define a custom element class, and as you parse, you pass around the parser’s delegate to each element as it’s being filled in. For example, when you encounter a child element, you init a MyElement object, and then make that new element the new delegate. Similarly, when elements end, you return the delegate to the parent element.

So this is nice, but it’s still kind of heavy. At the moment, I’m parsing XML from a MapQuest result (via their XML protocols), and wanted to try something a little lighter. Moreover, I wanted to be able to get at the parsed data with KVC, so I could just provide a key-path of the form root.child.grandchild. As an experiment, I tried parsing everything into a deeply-nested NSDictionary, which easily supports KVC.

After an hour or two, the idea basically works, though I’ll be the first to tell you this is sloppy code (I’m sure I’m leaking some element-name strings, but neither I nor the Clang Static Analyzer has found them), it loses the order of siblings (which I don’t care about), and it doesn’t yet handle multiple child elements with the same name (which would get into the indexed accessor pattern). Also, the character data is kludged into a pseudo-child called value, whereas using a custom element class would allow you to more carefully distinguish an element’s text, child elements, and attributes.

Basic idea is to keep a master dictionary for the parsed doc, parsedResponseDictionary, the current path being parsed, parseElementPath, and a mutable string for the current element’s character data, currentCharacters, which can arrive over the course of multiple callbacks.

Here are the essential delegate methods:


- (void)parserDidStartDocument:(NSXMLParser *)parser {
	NSLog (@"didStartDocument");
	[parsedResponseDictionary release];
	parsedResponseDictionary = [[NSMutableDictionary alloc] init];
	parseElementPath = @"";
}

- (void)parser:(NSXMLParser *)parser 	didStartElement:(NSString *)elementName
		namespaceURI:(NSString *)namespaceURI
		qualifiedName:(NSString *)qName
		attributes:(NSDictionary *)attributeDict {
	NSLog (@"didStartElement:%@", elementName);
	NSMutableDictionary *newElement = [[NSMutableDictionary alloc] init];
	NSMutableDictionary *parent;
	if ([parseElementPath length] == 0) {
		NSLog (@"parent is root");
		parent = parsedResponseDictionary;
	} else {
		NSLog (@"need parent %@", parseElementPath);
		parent = [parsedResponseDictionary valueForKeyPath:parseElementPath];
		// note valueForKeyPath: sted valueForKey:
	}
	[parent setValue:newElement forKey:elementName];
	[newElement release];
	NSString *newParseElementPath = nil;
	if ([parseElementPath length] > 0) {
		newParseElementPath = [[NSString alloc] initWithFormat: @"%@.%@",
			  parseElementPath, elementName];
	} else {
		newParseElementPath = [elementName copy];
	}
	parseElementPath = newParseElementPath;
	NSLog (@"new path is %@", parseElementPath);
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
		namespaceURI:(NSString *)namespaceURI
		qualifiedName:(NSString *)qName {
	NSLog (@"didEndElement:%@", elementName);
	if (currentCharacters) {
		NSMutableDictionary *elementDict =
			[parsedResponseDictionary valueForKeyPath:parseElementPath];
		[elementDict setValue: currentCharacters forKey: @"value"];
		currentCharacters = nil;
	}
	NSRange parentPathRange;
	parentPathRange.location = 0;
	NSRange dotRange = [parseElementPath
		rangeOfString:@"." options:NSBackwardsSearch];
	NSString *parentParseElementPath = nil;
	if (dotRange.location != NSNotFound) {
		parentPathRange.length = dotRange.location;
		parentParseElementPath =
			[parseElementPath substringWithRange:parentPathRange];
	} else {
		parentParseElementPath = @"";
	}
	parseElementPath = parentParseElementPath;
	NSLog (@"new path is %@", parseElementPath);
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	NSLog (@"foundCharacters");
	if (!currentCharacters) {
		currentCharacters = [[NSMutableString alloc]
			initWithCapacity:[string length]];
	}
	[currentCharacters appendString:string];
}

Using the sample request from MapQuest’s API docs, the parsed NSDictionary looks like this:


2009-09-29 12:34:40.263 MapQuestThrowaway1[6077:207] parsed dict:
{
    GeocodeResponse =     {
        LocationCollection =         {
            GeoAddress =             {
                AdminArea1 =                 {
                    value = US;
                };
                AdminArea3 =                 {
                    value = PA;
                };
                AdminArea4 =                 {
                    value = Lancaster;
                };
                AdminArea5 =                 {
                    value = Mountville;
                };
                LatLng =                 {
                    Lat =                     {
                        value = "40.044618";
                    };
                    Lng =                     {
                        value = "-76.412124";
                    };
                };
                PostalCode =                 {
                    value = 17554;
                };
                ResultCode =                 {
                    value = B1AAA;
                };
                SourceId =                 {
                    value = ustg;
                };
                Street =                 {
                    value = "[3701-3703] Hempland Road";
                };
            };
        };
    };
}

More importantly for current experimentation purposes, this lets me grab values from the parsed dictionary with KVC-style access:


NSLog (@"key-val test: lat long is %@, %@",
   [parsedResponseDictionary valueForKeyPath:
	@"GeocodeResponse.LocationCollection.GeoAddress.LatLng.Lat.value"],
   [parsedResponseDictionary valueForKeyPath:
	@"GeocodeResponse.LocationCollection.GeoAddress.LatLng.Lng.value"]);

That code produces the desired result:


key-val test: lat long is 40.044618, -76.412124

It’s not pretty, but it’s also not a lot of code, and allows me to get on with getting and processing the result data rather than dancing around with fancy XML parsing for a day or two.

Lack of posts lately… heads down on an iPod game. It’s built up of mini-games, about half of which are done. Today, I’m facing the problem of having to create a mini-game that uses some of the metadata in the iPod library that can’t be directly queried. So, I have to go over every song in the library and perform my own analysis.

Obviously, this would be death to at startup or in the middle of the game. Walking my 700-song library takes 6-7 seconds, and users could have far more songs.

Cut to the win: NSOperation makes it easy to do stuff on threads, without having to, you know, write your own pthread stuff.

As a test, I wrote a subclass of NSOperation to perform a simple analysis on the library: count the number of songs that have “the” in the title. Here’s the -main method:


-(void) main {
   NSDate *beginDate = [NSDate date];
   NSLog (@"*** DYDeepLibraryAwarenessOperation is cogitating and ruminating");
   // test - count titles that have the word "the" in them.
   int theCount = 0;
   MPMediaQuery *allsongs = [MPMediaQuery songsQuery];
   NSLog (@"Thinking about %d songs", [allsongs.items count]);
   for (MPMediaItem *item in allsongs.items) {
      NSRange theRange = [[item valueForProperty:MPMediaItemPropertyTitle]
         rangeOfString: @"the" options: NSCaseInsensitiveSearch];
      if (theRange.location != NSNotFound) {
         theCount++;
      }
   }
   NSLog (@"*** %d songs in the iPod Library contain the word \"the\".", theCount);
   NSLog (@"*** DYDeepLibraryAwarenessOperation has achieved enlightenment (in %f sec).",
         fabs ([beginDate timeIntervalSinceNow]));
}

Then, as the app starts up, the operation is run as part of an NSOperationQueue


awarenessOperation = [[DYDeepLibraryAwarenessOperation alloc] init];
operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:awarenessOperation];
NSLog (@"DYDeepLibraryAwareness set up NSOperationQueue");

Here’s the output when the code is just left to run by itself (I’ve taken out the date, classname, and line number from the output for space):


15:30:47.979 DYDeepLibraryAwareness set up NSOperationQueue
15:30:47.976 *** DYDeepLibraryAwarenessOperation is cogitating and ruminating
15:30:48.238 Thinking about 740 songs
15:30:54.586 *** 168 songs in the iPod Library contain the word "the".
15:30:54.589 *** DYDeepLibraryAwarenessOperation has achieved enlightenment (in 6.613482 sec).

Perhaps more importantly, and what I can’t show in a blog, is that this other thread does not interfere with the GUI, or with queries to the iPod library from the main thread, which are done to set up and play the first mini-game. So this means that the iPod library server can handle multiple concurrent requests (yay), and that I can do the heavy lifting to set up later games while presenting and playing the simpler ones.

Next Page »