Rss

Archives for : location

Pulled!

It’s iOS 7 GM Day! Apple is calling on developers to submit their iOS 7-enhanced updates!

And instead, I just pulled my two apps from the App Store.

OK, too much drama. But the fact is, I haven’t updated my stuff in years, it makes no economic sense to, and the stuff is too obviously dated to keep selling.

Continue Reading >>

Directions and Disintermediation

Apologies for a double-post to iDevBlogADay, but I didn’t want to hit the wider audience with two straight anime/manga-related iOS blogs, and I was way behind on entries for the first few months of the year anyways.

With WWDC and presumably iOS 6 approaching, John Gruber looks for obvious gaps in iOS to fill, and in Low-Hanging Fruit, he doesn’t report many. Following on reports that Apple will switch from Google to an in-house Map provider, he writes that while controlling such an essential functionality is crucial, the switch has to be flawless:

This is a high-pressure switch for Apple. Regressions will not be acceptable. The purported whiz-bang 3D view stuff might be great, but users are going to have pitchforks and torches in hand if practical stuff like driving and walking directions are less accurate than they were with Google’s data. Keep in mind too, that Android phones ship with turn-by-turn navigation.

This was an interesting graf to me, because Gruber usually gets the details right, and this time he’s pretty far off.

Start with “users are going to have pitchforks and torches in hand if practical stuff like driving and walking directions are less accurate than they were with Google’s data.” That’s a fundamental misunderstanding of what the iOS SDK provides. Neither Map Kit, nor any other part of the SDK, provides directions. Map Kit only provides map tile images, and a “reverse geocoder” that may be able to correlate a given location to a street address or at least a political region (city, state/province, country). There’s nothing in Map Kit that knows that a yellow line is a road, a blue polygon is a lake, a dashed line is a state or country border, etc. More details in my write-up of writing Road Tip and my Bringing Your Own Maps talk from 2010, but the long-and-short is that any app that provides directions has to be getting its data from some source other than Map Kit, probably a web service hosted by Google, MapQuest, Bing, etc.

I also did a little research to see what the Android API for this stuff looks like, assuming it was easier, and was quite surprised to see that it’s not. A DrivingDirections class was apparently removed after Android 1.0, presumably due to the fact that implementing it required third-party data (TeleAtlas, NAVTEQ) that Google wasn’t in a position to redistribute for free. The suggested workarounds I’ve seen are all to either license a third-party directions API (embedded or webservice), or to make a specific request to maps.google.com and scrape the result as JSON… in other words, to use Google as the directions webservice and not worry about the terms of service. I only spent about 15 minutes looking, but it didn’t appear to me that getting directions is any easier for Android developers than it is on iOS. So while Gruber notes that “Android phones ship with turn-by-turn navigation”, that seems to be a user feature, not a developer feature.

So, on the one hand, third-party apps probably won’t change right away, because they haven’t counted on iOS for their directions anyways. But maybe that’s an opportunity: if Apple controls its own data, maybe it could offer an easy-to-use directions API, consisting of easy Objective-C calls rather than JSON or XML parsing like webservice clients have to do now.

There might be a licensing snag too: using Map Kit today means that developers are implicitly accepting the Google Maps terms of service. If iOS 6 switches providers, the terms presumably change as well, and I wonder how that would be handled. Would creating a MKMapView magically default to the new Apple maps in iOS 6 (and thereby inherit new Apple terms of service), or would developers maybe have to resubmit or add a credential to their apps to get the new maps?

And taking maps in-house has other interesting side-effects. The other day when I was out with my kids, I searched for “Arby’s” in Jackson, MI and the result appeared on the map as a “Sponsored Link” (I can’t re-create it here at home… maybe it’s only an ad when you’re physically nearby?). The money for that ad presumably goes to Google, a revenue stream that will start to dry up if Apple provides its own business location data when searching maps. We’ve also heard that Siri could hurt Google search if people start performing speech-based searches rather than keying terms into google.com via the mobile browser.

And isn’t this exactly what Google was afraid of, and why they felt the need to create Android? With iOS, Apple is in a position to disintermediate Google. And with Apple’s wrath towards Android, the company may be more interested and willing to do so than it might otherwise have been. Smooth move, Andy.

Having a Moment

Please indulge me a Whiny Little Bitch moment. I will try to keep it short, but it will be petulant, jealous, and immature. You have been warned.

TUAW’s “RoadAhead is a different and clever nav app” talks up the new RoadAhead app, which finds upcoming services at US freeway exits. They’ll benefit from the exposure, and being free will certainly help.

But contrary to some of the reviews, the idea of an exit finder that figures out what road you’re on and what direction you’re going and only shows you upcoming exits isn’t new. That was the whole point of the Road Tip app I did a year and a half ago.

Obviously, I think that’s the way to go, and I’m glad RoadAhead does the same thing. I think it’s lazy when apps just do a radius search for this kind of thing, because that way you’ll find stuff that’s behind you, or is hopelessly far off the freeway. The idea of “finding stuff along a freeway” is a specific concept, and this app is true to it. Plus, figuring out where the road goes, winding back and forth, and dealing with things like name changes and state boundaries is a genuinely interesting problem to solve.

One point of difference is that RoadAhead can’t resist the temptation to pile on lots of icons and pinch-zoomable maps and other bling. As soon as you go down that road, the app becomes a distraction, and like so many others in this field, they explicitly say it’s unsuitable for use while driving:

One last, important thing – USING YOUR PHONE WHILE DRIVING IS A BAD IDEA (and in some states illegal). Hand your phone to your passenger and ask them to navigate. Please use good judgment when using your phone in the car.

I agree that using your phone while driving is a bad idea, but I still designed for it because I think people will do it anyways, regardless of what’s in your app description, EULA, or whatever. Road Tip uses spartan layouts, large text and buttons, and a design philosophy that everything should be accessible via one-thumb navigation. This frustrates my colleagues who say Road Tip would sell if I added various features, like the ability to save your search locations and drill deeper into them later (like remembering what’s at the exit when you pull off, so you can find stuff after you check into your hotel). My point was to keep the app as simple as possible for use while moving; once you’re off the road, you’ll be able to use the deeper functionality of other mapping apps.

So yay, I’m true to what I want my app to be… and I’m the only one who wants it like that.

The other thing is that RoadAhead is free, so I can’t figure how this app pays for itself. I decided early on that RoadTip couldn’t be ad-supported, because showing an ad to a driver would be unconscionably distracting. So I opted for a pay model.

And that brings up my other point about my exit-finding competitors. I’ve never downloaded them — I don’t want to get in a situation where I could willfully or inadvertently plagiarize them (this is why I also don’t read other people’s books on topics I’m covering) — but I’ve looked through their descriptions and legal terms, and I’ve never been able to figure out where apps like RoadAhead and iExit get their location data. That I use MapQuest is obvious; I’m contractually obligated to have my users accept the MapQuest TOS, and I have to put a “Powered by MapQuest” notice on any screens that result from their data.

So where are the other guys getting their data from? As I wrote in Bringing Your Own Maps, my research revealed that the map data providers’ terms of service for their free services always prohibited use in paid iPhone apps, either because they required callers be free and public web apps (Google Maps TOS, section 9.1.1(a)), or prohibited use based on GPS location (i.e., “present or alert an end user to individual maneuvers of a route in any way that is synchronized with the end-user’s sensor-based position along the route”, as in the Bing Maps TOS, section 2.i), or both, or something else.

This stuff is why I had to enter into commercial licensing with MapQuest (whose terms and API I liked best). Not sure what the competition is doing. Maybe they found some way to get free data legally, maybe they’re getting away with not… it’s not really my business I suppose. But if it does turn out I’m the only one playing by the rules, yeah, I’ll be a little pissed.

I keep calling them the competition, and I suppose that’s not accurate. I’m not competing at all… I’ve totally capitulated. I’m unlikely to put any further work into Road Tip, outside of possibly switching to the new I-AP subscription model that restores across devices (good for my long-time users, bad for me because it means sinking more time into this miniscule-selling app). If Ford ever followed through with opening their Sync / MyFordTouch API to third parties, I might make Road Tip work with it, but then again, I might do so just for my own use and not release it. It’s not something that I really feel like putting any further public work into.

OK, whining done. Back to your regularly scheduled blog.

Of user-local programming

A Detroit-area “Mobile Monday” group cross-posted an event to the Ann Arbor CocoaHeads group (which I attend regularly), and it’s interesting enough to make it worth my time to drive across the state twice this week. Which, in addition to traveling to Philadelpia for Voices That Matter: iPhone Developer Conference on Friday, is probably too damn much travel for one week.

[Aside: we really need a West Michigan CocoaHeads group…]

The event is going to feature a presentation from Ford on SYNC and the API they’re exposing to developers of third-party applications. At 360iDev, fellow AA CocoaHead Tom Hoag was on me to check out SYNC, and since we’ll probably have it on our next car, I would naturally be attracted to it anyways. Looking at the developer welcome page, they talk about planning to distribute apps through existing app stores, so whereas I originally thought a SYNC SDK might involve writing apps to run on the car’s CPU, maybe what it actually means is more of a protocol to be called via the iOS External Accessory framework. That would actually suit my needs much better: I’ve long wanted to voice-enable Road Tip, and being able to just connect to SYNC and let it do the voice recognition and speech synthesis is conceptually straightforward and would be very compelling. In fact, it would be more practical for the user to use SYNC’s driver-optimized microphone and the sound system’s speakers than to use in-built iOS speech frameworks (which aren’t yet available to third-party apps anyways).

But that’s actually beside the point I wanted to make, which is this: to sign up for the event, I had to join the Mobile Monday meetup group, the process for which posited this question: “Why are you interested in mobile.”

I answered: “I’m not.”

Specifically, I’m not interested in mobile, per se. Over the summer, I was talking with a high school friend of mine, Raman, who’s had a long and successful career at Microsoft. One of the things we found we both liked is the idea of having our code running where the user is. This isn’t mobility, exactly, because we both like desktop programming too. What we like is having our code executing on a device that’s where the user is, in direct interaction with the user, rather than on a server somewhere.

For the sake of coining a term, I’m calling this “user-local computing”.

I think this is an important turf to stake out becasue there’s been an almost smothering focus on the web and server-side programming for the last decade, that a lot of us feel like we’ve been left behind or overlooked. Sure, I can do server-side stuff — I’ve written servlets and SNMP traps and web services — but what I enjoy is the kind of application that’s interacting directly with a user, not looking up some database record that something else is going to present, or sending some HTML that’ll get rendered a second or two later and just sit there. Other people like that. Most people like that. In some camps, the server side is the default point of view, to the point of myopia (looking at you, Java community).

But not everything can be a webservice. Not everything should be.

There is a tangible difference to code that is executing in the user’s immediate presence, stuff that responds immediately to input, that manages resources locally. The web is getting closer — Google Instant Search is a remarkable user experience — but it still isn’t, and probably can never be, as responsive as work being done on the user’s own CPU.

There are also creative tasks that seem ill-suited to remote performance. It may well be possible to make a browser-based IDE, with source files stored on and executables built on a remote machine… but would it really be a good idea? Now let’s up the bandwidth: could you build a video editor in a browser, sending all your source media back and forth across a network connection? I’m sure someday it’ll be possible, probably soon, but I think it’ll be a long time before it’s actually a good idea.

In a way, this is a sort of retro outlook for those of us in our 40’s: the old paradigm of computing was based around creative tasks executed locally: Visicalc, WordPerfect, MacPaint, PageMaker. With the dictate that everything has to be network based, we’ve gotten more in a mindset of applications that collect and consume data from various network sources. And that’s great. But the idea of empowering your user to actually do stuff still matters, and while it may seem old-fashioned, a lot of what really matters involves clearing out the noise and distraction of the network and concentrating on doing something yourself. Notice how the new trend in text editors is to dispense with formatting, menus, toolbars, and all other distractions: it’s just you and your text. This is an ideal application of user-local computing: cut the crap, just let me focus on the here and now. “Here” being wherever I am, and whatever CPU happens to be handy.

So I’m interested in desktops, tablets, and iPhones not because of some high-falutin’ idealization of the power of mobile computing… I like them because they put my code where the user is, whether that’s at a desk, a couch, walking around, or driving in a car.

Road Tip 1.3 Coming… Eventually

Not that I think very many people were wondering, but I do have an update to Road Tip nearly ready to go. It’s just going to take a while to get it finished and out the door.

The reason, frankly, is that it is literally not worth my time to work on it. Once I’ve paid my quarterly bills to MapQuest for their data service (without which, I wouldn’t have an app at all), my cumulative return on Road Tip is less than I make in four hours of contract programming. I can only justify time on it based on its intellectual interest, because as a professional endeavor, Road Tip has been a five-figure loss for my family’s finances once I account for expenses and the time I took to develop it.

I’m tempted to say you’ve seen the last of me as an indie iPhone developer, but you never know… I vowed not to write another computer book after the wobbling grind that was Swing Hacks, and yet I’m still at it. Best to say that indie development is now much more of a hobby in my mind, completely subordinate to contract programming and writing.

Still, I can’t help myself: I use Road Tip, and want to make it better, if only for myself. So here’s what I’ve gotten done.

  • iOS 4 foregrounding/backgrounding support – This is the big one, of course, and it’s been interesting. I handle backgrounding by opting out of location updates while backgrounded, to save the battery. This causes a number of problems at wake-up time, mostly related to the fact that any data in memory is almost certainly out of date and useless. I handle foregrounding by forcing the user back to the front screen (clearing out the presumably old data on any result page they might be on), and asking Core Location for new location data. Core Location provides timestamps in CLLocation objects, so it’s easy enough to determine if your data is old. Thing is, while I can get an updated coordinate quickly, the reported course will often be wrong (for reasons described in my earlier post on Road Tip), so I have to hold off on enabling the GUI until I have a higher level of confidence that the data supplied by Core Location is accurate.

  • HUD improvements – The “spinner” while you’re waiting for data is interminable on Edge when there’s a lot of data coming back, so I’ve added some status text to give you a better idea of what progress is actually being made. Here’s what that looks like:

  • Freestyle Favorites – The last of the features that I “always meant” to add, this feature lets you add any brand as a favorite, directly from the map screen. What this means is that you’re no longer limited to just the best-known brands from the Settings UI. You can add whatever business names you find along your way. For example, in this screenshot (taken on a family vacation last week), I could use the “make favorite” button to add the “Kahunaville” restaurant inside the Kalahari Resort in Wisconsin Dells.

    Freestyle favorites also work in tandem with the main list of favorites. If you tap “make favorite” on a brand from the canned list (like “Burger King” or “Shell”), it will just set the favorite in the main list, and you’ll see that the next time you go to the Settings app.

    BTW, the one decent critical review I got on Road Tip (as opposed to the usual anonymous 1-star haters) complained about my use of the Settings app. That was a consequence of one of my top design goals: nothing finicky in the main app. Meaning that I wanted the main app to be very “glanceable” and work with coarse gestures, not the kind of UI that requires your full attention. This is, after all, meant for use in a moving vehicle: anything distracting is bad. Heck, if iOS had a speech API, the app would be better off being completely hands-free. Anyways, scrolling through a list of brands to pick favorites is something you can do at home, and therefore can be done the Settings app (per Apple’s guidance to developers, I might add). But looking at it, I thought that putting a big “make favorite” button (which can also be un-set with a second tap) was a sufficiently coarse, non-distracting action, and potentially very handy.

  • Retina-friendly display – Despite the fact that you should be looking at Road Tip’s screen as little as possible, I did join Glyphish’s Kickstarter project so I could get Retina-display-friendly @2x icons. Kimberly Daniel of Vantage Point Creations also reworked the app icon to have a nice bezel and pre-rendered gloss

  • Purchase UI improvements – During an in-app purchase talk at CocoaHeads Ann Arbor, our group leader got confused by the long latency in pressing the “buy” button and getting the system-provided confirmation dialog. A new “purchasing…” HUD fills that gap.

All that work is done. So what’s the delay? Bugs, mostly. The backgrounding/foregrounding support has turned up some issues that either are new in iOS 4 or were always there, but never exposed because the app used to get re-launched when it was needed, rather than running indefinitely. The biggest problem at the moment is a bug where the “view all upcoming services” mode continues to get old results from whatever the first location was you searched from. Actually, it gets the freeway name right (even if you’ve since turned onto another freeway), but provides exits from the previous search results.

I’m also getting some “junk” characters in some freeway names that aren’t getting stripped out, as seen at the top of this screen.

I think it might take one full day to work through these bugs, and maybe another day to get everything set for an update submission (I have to re-write some of the help text and re-shoot some of the screenshots). So I’ll get to it at some point. I just don’t know when. Probably in the next few weeks… no promises, though.

What you missed at Voices That Matter: iPhone Developers Conference

Last weekend was the Voices That Matter: iPhone Developers Conference in Seattle, put on by Pearson, who’s publishing our Core Audio book. However, co-author Kevin Avila handled the Core Audio talk for this conference, so I took on two topics that I had fairly deep knowledge of, thanks to my work on Road Tip.

Core Location and Map Kit: Bringing Your Own Maps

This talk starts with a basic tour of Core Location’s more or less straightforward means of getting information about your current location, getting updates as the data gets better (i.e., GPS kicks in) or you move. Then we got into Map Kit and how to get map images of any given location. The sample app for this takes a list of Apple Store locations, which I converted from CSV to a plist and stuck in the app bundle, and shows you the nearest store to a given start location. Each time you hit a + button in the nav bar, it drops the next closest store as a pin on the map and re-scales the map so that all pins are visible.

This is where it gets good. Starting from a canned location at my house, the closest Apple Store is here in Grand Rapids. The next two that come up are outside Detroit (Ann Arbor and Novi). The fourth closest store is in Milwaukee. The comedy is when you see this on the map — Milwaukee is close only if you ignore the fact that going there would involve driving 100 miles straight across Lake Michigan. Since ferries across the lake are slow and expensive, and run only in Summer, you would probably drive through or around Chicago to get to Milwaukee… and go right past 7 Apple Stores in the process.

Calculating as-the-crow-flies Apple Store distances from GRR

This is a common mistake to make — I forgot to show in my slides that the apple.com retail store finder does the same thing. Still, as-the-crow-flies distance calculations, paired with map images, can be a problem. Map Kit doesn’t know anything about roads, bodies of water, geographic features, political borders, etc… all it does is serve up images, and provide a touch UI to interact with them.

The last third of the talk is about “bringing your own maps”, meaning integration with third party map data providers to get some navigational intelligence into your app. The final code sample uses the MapQuest directions web service to get actual driving distances to the first few hits, and to keep the list of locations sorted in order by driving distance. This not only keeps me from going to Milwaukee, it even smartens up the earlier results: being right on I-96, the Novi store is now my second closest result, and as it turns out, even Indianapolis is a shorter drive than going around the lake to Milwaukee.

Calculating drivable Apple Store distances from GRR

In-App Purchase: Best/Worst Thing Ever

My second talk was on In-App Purchase. In many ways, it covered the same hard-earned experience that I covered in An In-App Purchase Brain Dump. I spent a little time on both the iPhone Provisioning Portal (to set up an AppID, authorize it for I-AP, and provide enough of an app submission to create purchase objects, without actually going into the review queue), and on iTunes Connect (creating purchase products). The sample app simulated an online social game, minus the game, in which users might be able to purchase digital goods like virtual clothes for their avatar. The example offered a tabbed UI with a blank view for the game, a table of purchased objects, and a table for the store. When you tap “store”, the app collects the available products with a call to Store Kit, and tapping one of them kicks off the purchase. If the purchase goes through, the item is added to the inventory page.

Purchasing an item with I-AP

Of course, this is easier and demos better because it uses non-consumable products, which have always been the best-understood and best-supported class of I-AP products. I did weigh in against the still-broken subscriptions, and how they have to be restored to a user’s many devices, even though restoreCompletedTransactions doesn’t support subscriptions, and nothing in Store Kit gives you a key you can associate with the user to save the purchase on your own server.

Erica Sadun talked with me before this session and mentioned an interesting workaround. Get your user to purchase a non-consumable item, and persist its transactionId. When they purchase subscriptions, log the purchase and this other transactionId on your server. Then, when they restore on another device, they’ll get this non-consumable back and its transactionId, which you can then use to query your server for subscription purchases. Depending on the nature of your app, and how well you hide the clunkiness from the user, this could be a very viable workaround.

That said, I still think I-AP subscriptions suck and hope that Apple deprecates them soon.

The last part of the talk covers of doing commerce without I-AP, which is more viable than you might think. If you’re already selling digital goods from a web store, you can continue to do so, and make your iPhone app a rich client to your web-vended content. In this case, you’re already doing everything that I-AP offers, so there’s no reason to give Apple a 30% cut. For example, the various e-bookstores within Stanza don’t use I-AP — they go out to websites for O’Reilly, All Romance Books, etc., to complete the purchase. This might not be allowed for an iPhone-only app, but where the goods are available in multiple forms, it only makes sense for Apple to allow an iPhone app to be a rich client to such a store’s goods.

My final example is streaming anime: Crunchyroll sells premium content subscriptions on their website, and their free iPhone app lets you use the same login to get the same content. By contrast, The Anime Network has premium subscriptions on their website and via I-AP in their paid iPhone app, and credentials aren’t shared between the two. Worse, the iPhone app offers fewer videos for the same price. Their use of I-AP is bad for them (they’re paying Apple to process payments they’re already capable of handling), and bad for their users. WTF?

Coming next: my 360iDev slides, and the much-anticipated cleaned-up Core Audio sample code

Another conference, two more sessions

My editor on the Core Audio book passed along my name as a potential speaker for the Voices That Matter: iPhone 2010 conference… but not on Core Audio. My co-author, Kevin Avila, has that covered. And I’ll be covering CA two weeks prior at 360iDev, so why would I want to put all that work into a talk and use it twice, when I can put lots more work into two talks. That would be too easy.

Oh, did I say two talks? I meant three. Chuck suggested I do a location/mapping talk based on my experiences developing Road Tip, and after agreeing, I noticed the schedule had a spot for an In-App Purchase talk. Foolhardy me, I said to the organizers, “hey, if you don’t have a speaker for that topic, I can take it.” So now I’ve got two talks for VTM:i.

The tricky thing is, the VTM talks are going to be pretty easy to put together, given that I’ve already written extensively about mapping and in-app purchase here on [T:c];. Heck, I did I-AP last week for Ann Arbor CocoaHeads, and I think I only swore three times.

The Core Audio talk, which comes two weeks earlier at 360iDev, will actually be the hardest to put together because the proposal and topic are still pretty nebulous — “advanced Core Audio” — and because I’ll want to pull, you know, actual advanced stuff into it. On the other hand, it’s great that there’s a “Three Core Audio Hello Worlds” earlier in the conference, covering AVAudioPlayer, OpenAL, and Audio Units to some degree, so I can elide some of the basics and get right into the hard-core.

Thing is, I now have, what, three weeks to put the hard-core together. Alas, another crunch.

Oh, and here’s a promo code for VTM:i: PHASPKR. I don’t have a code for 360iDev, but if you follow 360idev on Twitter, you might catch one flying by.

The Long and Winding Road Tip

Road Tip 1.1 is out today after a week in review, and that provides an opportunity to talk about my first foray into location-based development.

The Idea

The origins of Road Tip probably trace back to a trip to Southern California last year with my family. We were on the way back to San Diego from Disneyland, looking to grab some dinner. All kids are picky, ASD kids doubly so, which meant we were on the lookout for a Taco Bell. Not exactly a rarity, but as it turns out, California doesn’t have those blue “what’s at the next exit” signs so common in the midwest and south. Moreover, they also don’t seem to allow billboards. The practical upshot is that if you’re looking for a particular brand of roadside service, well, good luck finding it. I actually ended up typing the search term “taco bell” into the Maps app while driving 65 MPH, a stupidly foolhardy thing to do, but such was my frustration.

The thing is, it actually gets worse: Maps is only smart enough to do a radius search: it can find Taco Bells near you, but can’t account for whether they’re five miles off the freeway, or five miles behind you.

So later that Fall, I came on the idea of making an app to find stuff at exits, by taking into account your direction of travel, and looking for what would be on your way. In general, my plan would be “find exits, then find stuff near them”.

Building the thing

This actually turns out to be an interesting problem to solve. Most of the map APIs out there are built around the concept of “given a destination, how do you get there”, and give you options like taking certain kinds of roads (avoiding traffic, keeping Rain Man off the freeways, etc.). But this app is different: instead of “given destination, find a route”, it’s “given a route, find some destinations”. It also twists the usual logic of point-of-interest searching. Instead of “find stuff near a point”, it’s “find stuff along a route.”

The iPhone includes APIs for working with locations, and for providing map images, but not for working with directions. This is what Apple meant in their 2009 introduction of the iPhone 3.0 SDK when they said “bring your own maps”. I previously detailed my search for a map provider, based on the needs for an iPhone mapping application: an API that can be called from an C/Objective-C app, rich search functionality and a deep database of POIs, and license terms I can live with. I think a lot of people overlook this last point: most map websites are free, but using their data in a commercial application usually is not. I mean seriously, come on, do you think they pay their people to amass all this data and are then just going to give it to you because they like you, or because you make some speech about “information wants to be free” or some garbage like that? Puh-leeze.

Most people I know tend to default to Google Maps, but looking at it from the API point of view, they were out of the running early. They seem primarily interested in their JavaScript API, which can’t usefully be called from an iPhone app (unless you’re just writing a webapp). Their HTTP webservice-ish API has a lot of holes, and their terms didn’t seem welcome to commercial licensing.

I ended up contacting MapQuest about a commercial license, and went with their service. They have a number of APIs for various scripting languages, as well as a machine-to-machine XML API that these are all based on. I realized that as long as I was willing to send and parse a lot of XML, this would suit my needs perfectly well. After getting good results with a few proof-of-concept projects, I got in touch with MapQuest and worked out a commercial license to use their data in the app. In addition, I got access to their developer and marketing support, which has been very helpful, as I’ll get to in a little bit.

Building Road Tip

I had a few specific goals for Road Tip. I wanted it to be as un-fussy as possible. If it was going to be so distracting that it could only be used by a passenger, then there’d be no point writing it: just have your spouse or your kids do the Maps app instead. Using a smartphone while behind the wheel may not be safe, but since people (myself included) are going to do it anyways, I wanted to make the app as non-distracting as possible. So my design goals were:

  • Glance-able: get the info you need with a 1-2 second glance at the screen, no longer than you’d look at a road sign or your radio.
  • One-thumb control: able to be used by holding it in one hand and interacting entirely with your thumb. That means no pinch-to-zoom, no typing. I also made most of the buttons bigger than the default, to make them more hittable
  • Nothing extraneous: all I want is stuff on my current freeway, not what I might find if I turn off onto other highways. And it goes without saying that an app like this cannot use banner ads.

This accounts for the high-contrast, minimalist GUI. It’s gotten me bad reviews on the App Store for not jumping into the bling arms race, but the point of the app is to not look at it for any more than a second or two. Honestly, the ideal UI for this app isn’t visual at all: it would be far more useful to do voice synthesis and recognition, having the app read out potential destinations and letting the user speak his or her choices. If we get speech in iPhone SDK 4.0, you can bet this will be something I adopt for a Road Tip 2.0.

Finding Your Way

So then there’s the whole idea of the app logic. It’s not that hard to find exits in the map data: with MapQuest’s “navt” (Navteq) coverage set, you just search for objects of display type 1736. An Exit Finder demo from MapQuest illustrates using a different database to do the same thing (this example provides road names for the exits, instead of just numbers, something I adopted in 1.1). The problem is to find the right exits. In my first version, I found all exits in a cone-shaped search region ahead of the user, not knowing what freeway they were part of. The problem was that when your freeway intersected another, you’d get info for exits that you couldn’t actually take or wouldn’t do you any good (such as the other freeways’ exits onto your current freeway). I tried to filter these out by examining exit numbers, but this approach was filled with both false positives and false negatives.

My road tests proved this out pretty quickly. Consider the intersection of I-196 and US-131 in downtown Grand Rapids:

The exit numbers only differ by about 8, so a loose rule like “ignore exit numbers off by more than 20 from other nearby exits” that works well most of the time, doesn’t work here. A worse test is US-127 south through Lansing, MI:

If you’re heading south on US-127, exit counting totally doesn’t work, because as I-496 comes in from the West, its exit numbers take over. So you count down through the 70s when suddenly, Trowbridge Road is exit 8 and the exit numbers start counting up, to Jolly Road at exit 11.

It gets even worse if you use the relative location of exits to set up your next search for more exits, as I did. In that case, finding exits for the wrong freeway ends up sending the whole search off-course.

So what I needed was to really keep track of the road the user is actually on. MapQuest’s engineers gave me some advice for a technique that is harder and costlier, but maximally accurate: find the road segments themselves. The logic I ended up going with figures out the name of the road you’re on, then searches ahead and filters and sorts only segments that match this road. The practical upshot is that Road Tip can keep its search limited to the current freeway, ignoring intersecting freeways and even stretches where multiple freeways run together.

To that end, here’s an internal debugging screen (using the built-in map tiles, sorry MapQuest), that shows how this kind of logic handles the US-127 torture test without turning onto either I-496 or I-96.

This approach also has the advantage of handing twisting roads really well. I use a series of searches for road segments, each one set up by the direction vector of the last segment of the previous search, so it doesn’t lose track of twisting roads (and, when searching all services, can kick off the gas/food/lodging search while still searching for more road segments). Here’s the debugging screen again, this time looking at I-80 East near Truckee, CA, where it goes mostly North (and occasionally a little South and a little West) on the way through the Sierras and into Nevada.

Where’s my dinner?!

With a representation of the road accurate to a matter of feet/meters, I can then search for exits, and perform radius searches around each of them for restaurants, hotels, and gas stations.

I decided there were two primary use-cases to address: either the user knows they want to exit soon and wants to look exit-by-exit for what’s available if they get off now, or the user wants to know what services are on the road ahead, regardless of distance.

For the first case, an “exits” button lists the upcoming exit as a grouped UITableView:

Each row of the table is a button. Tap it to bring up the exit’s services, presented as a tab view of gas / food / lodging / all.

For the “hold out for Taco Bell” case, you take the other navigation branch, tapping “services”. This presents a short list to indicate what you’re searching for: gas, food, lodging, or “favorites” (which you set up in the Settings application, ideally before you go driving, since it’s detail-oriented and highly distracting). This view organizes service names (restaurant and hotel chains, for example) by the exits where their locations can be found:

In either branch, the final page is a map of your selected service, relative to the exit:

What’s Next?

So that’s Road Tip as of 1.1. I’m pretty happy with the utility of the app, even if the App Store reviews and ratings are a mixed bag. I built it to suit my needs, and use it on all my long drives, and if it’s not as bling as so many tricked-out iPhone apps, well, that’s really contrary to the point of the app.

The one most useful criticism so far is that the app has a very long startup time. I need to get the current direction in order to know which way to search for road segments, and the iPhone’s GPS can take up to 30 seconds to deliver that. Ugh! However, I’ve been experimenting in the last week to see if I can deliver a “good enough” estimate to start searching, even before I get the high-confidence course from Core Location. It’s a little tricky, because the the first update from CL is usually junk. For example, consider the following map:

When I run the app at my desk and I’m not moving, point 1 is the first location provided by GPS. Problem is, point 2 is where I actually am. Draw a line between them and you’d infer that I’m moving east-southeast. So to do the estimate, I have to ignore the first update and wait for the second and third unique locations before estimating the direction of travel. Still, on the highway, I’m getting this in 5-15 seconds, which is about half the time of waiting for Core Location. So that’s going into 1.2.

After that, I don’t know… I’m very busy with a day-job Mac contract, and I really want to get back into media programming (and the Core Audio book), so this may have been a one-time-only foray into location-based iPhone app development for me. I can’t say at this point if it’s going to end up making financial sense to have spent so much time developing it and putting it out there on the App Store, but at least it’s been intellectually rewarding.

Road Tip approved, arrives Monday

Great news: Apple has approved Road Tip, my app for finding gas, food, and lodging at upcoming exits. It will appear in the App Store on Monday.

In anticipation of the debut, I’ve set up:

The only glitch is that the in-app purchases for extending service beyond the trial period remain an issue of contention, for reasons discussed earlier. The app comes with a three-month trial period, so there’s time to resolve this before it becomes a problem.

An In-App Purchase Brain Dump

Oh thank goodness. Apple has finally come up with an API that’s a bigger pain in the ass than Core Audio. Namely, In-App Purchase. So no more bitching from any of you about kAudioUnitProperty_SetRenderCallback until you’ve tried validating a restored purchase.

I’m not complaining, not entirely, because both of these are inherently complex propositions, and the APIs are, by and large, designed to account for the hard parts of each problem domain.

That said, I spent as much time developing Road Tip‘s (née Next Exit) in-app purchase of continued mapping service as I did the app’s core functionality, so that’s gotta tell you that working through I-AP is no weekend project.

Apple’s programming guide is a decent-enough overview, as is the WWDC 2009 session, but I still felt like I ran into enough surprises that a brain-dump of tips and hints is in order.

  • Leave yourself a lot of time for I-AP. Seriously, it took me a person-month to get it all together.
  • The most critical decision is to figure out exactly what you’re selling and how it fits into I-AP’s view of the world. The one case that’s straightforward is “non-consumables” or “durables”, stuff that the user buys and has forever, like eBooks, or new levels in a game. Since these never disappear, Apple requires that you support copying these purchases to other devices, which you do with restoreCompletedTransactions. In fact, this is the only way you can do it: nothing in a purchase transaction tells you anything about the user (like their iTunes ID), so this method call is the only way of discovering a user’s previous purchases.
  • Other purchases are meant to be used up, like ammo in a game. If you buy 500 rounds of howitzer ammo on your iPhone, you shouldn’t be able to use it up and then magically restore it on your iPod touch. So restoreCompletedTransactions doesn’t work for these.
  • The biggest problem in I-AP is the design of “subscription” products. These are defined as consumable products that copy between a user’s devices. There’s an inherent design problem here: if something is consumable, there’s usually a degree to which it has been used up. How many rounds of ammo have I fired? How far into my one-year subscription am I? The degree of consumption is a state variable, something that can be tracked on one device, but can’t be communicated by means of a restoreCompletedTransactions call. Actually, it gets worse; restoreCompletedTransactions doesn’t return subscription products at all, so there’s no practical way for a copy of your app running on a new device to discover what subscriptions the user has purchased on other devices. I consider this a design bug and filed it with Apple (see the Open Radar copy).
  • Assuming you figure out a product model that works for you, you’re going to be working with at least three data sources:
    1. A list of product ids, perhaps saved with your app as a .plist or in a database, or available from the cloud
    2. The Store Kit APIs, which provide SKProduct objects for given ids and include localized name, description, and price. You’ll then present these products to the user and purchase them via other SK APIs.
    3. Apple’s validation webservice.
  • The last of these catches some people by surprise. This is easy to overlook, but the idea goes something like this: if you want an audit trail of user purchases, you need that data sent back to you somehow (since the purchases go from user devices to Apple’s servers, neither of which you control). You can log purchases by having the app call back to a server of yours, but how do you know that you’re getting called from a real copy of your app and not just some hacker somewhere? You call an Apple webservice at https://buy.itunes.apple.com/verifyReceipt with a purchase receipt received on the device, and get a response telling you whether the receipt is good or bogus.
  • Communicating with the Apple web service is done with JSON, and the purchase receipt needs to be sent as base64. I ended up using the MBBase64 category posted to CocoaDev to do the base64 encode on the phone, send that and other data to my web service. I wrote my web service in Java on Google App Engine, using json.org’s Java library to format my submits to the Apple web service and parse the responses.
  • Back on the device, one thing you’ll notice is that the Store Kit APIs are highly asynchronous: instead of blocking when you request products or make a purchase, you set a delegate or observer and implement a callback. It seems natural to put your commerce stuff in one central place in your app, and have that communicate changes to the rest of your app by means of delegates or NSNotifications.
  • Another surprise is that the payment queue (into which you put purchase requests) is persistent, so an unfinished purchase hangs around between application launches. This saves you from losing the user’s purchase if the app dies or loses its network connection in mid-purchase. Good thing. But it also means your observer will get called back shortly after you create the SKPaymentQueue singleton, even if your user hasn’t touched the purchase stuff.
  • You remove purchases from the queue by calling a finishTransaction method. You only do this when you’re good and sure the purchase has gone through. Apple’s WWDC session suggested not unlocking functionality until you hear back from your webservice. I thought that was a little strict: I’m unlocking when I get the “purchased” callback from the queue, but not finishing the transaction until I hear back from the webservice. If my webservice ever went down, the user would still have the unlocked feature, but the purchase would hang around in the payment queue, prompting a new validation attempt with my webservice every time the app comes up. That seemed more elegant to me.
  • A thought for you: how do you persist the “unlocked” state from one launch to another? You could write a .plist, a database record, or other local file, but that struck me as rather hackable by the pirate/jailbreak crowd. It’s also bad for consumables if a user could use up most or all of a consumable, then wipe the app (and your state data), reload, and get free stuff. I opted for putting this data — in my case an install date, which marks the beginning of a free trial period — in the keychain, whose data survives wipes (see the Apple devforum thread Keychain is now my best friend against in app purchase piracy). Only downside is that the keychain API is fairly hard to use, and Apple’s sample code is so determined to put a pretty Obj-C wrapper around the keychain, they actually make the material harder to learn (more info in this thread).
  • One thing that came up in the forum was the idea of when to try a restoreCompletedTransactions to get already-purchased items onto the current device. One developer said he or she planned to do this check at every startup, but I think that’s a bad idea: the user will typically be prompted for a password when you call restore, and that would be highly annoying to have to do all the time, especially as the need to pull old purchases to a new device is likely to be somewhat rare. I opted to make it a manual action, which means the user will only be prompted for a password as a direct result of a restore action.

  • Here’s another presentation concern: what should the purchase options look like? It seems like there’s an emerging consensus to put it in a “buy” button that’s placed at the right side of a table cell. This way, you can leave tapping the cell itself as a “preview” or “more info” gesture, like the iTunes application does, and make purchasing a more explicit gesture. Here’s what it looks like in a typical third-party app with in-app purchase, Weekly Astro Boy Magazine for iPhone / iPod touch




    Slight problem with this approach: it is actually fairly difficult to create a working button on the right side of a table cell. The API for accessory buttons doesn’t support rendering custom content into a right-side button (like “buy” or a price), so I opted to use a custom table cell, and a custom button class that can crawl the container hierarchy to find the parent table and call a selector when it’s tapped.




    Note that this image is debugging only: the published app will only present the user with the product that extends their service period by exactly one year.

OK, I think that’s all I’ve got to dump at the moment. Hope this helps people work through the challenges of I-AP. It’s an important API, but man it’s a struggle to actually put into practice.