Rss

Archives for : December2009

Conferences 2010

I’m speaking at two conferences in early 2010, as indicated by the badges in the right side column.

First is CodeMash in Sandusky, OH, January 13-15. I’m doing one four-hour “precompiler” tutorial on iPhone programming, and two sessions.

No, of course they’re not done yet. But here’s a bit of what I’m planning:

  • Introduction to iPhone SDK – I’ve decided that rather than put up some slides to walk through the basics, I’m going to teach the entire tutorial in Xcode and Interface Builder. This means I need to not only write my examples in advance (duh), but to pull together a bit more of a script so I know where to stop and explain things: “Objective-C uses square braces for method calls, which are really better thought of as message dispatches”, “IBAction is synonymous with void, but tells Interface Builder it’s OK to accept a connection to this method”, etc. I’m also thinking about how to cover the most truly useful material in four hours. I think I’d like to do the trivial browser in the first hour to do basic project building, IB, coding, etc., a tabbed app in the second hour to play with multiple view controllers, and a navigation app in hours 3 and 4, since those are so bread-and-butter. The nav app might use the webservice of the conference’s session list… if I think we can really pull off tables, networking, and XML parsing in two hours. Gonna have to rehearse to convince myself it can be done in a group setting in that kind of time.
  • How Do You Do That on iPhone? – This is just going to be a grab-bag of non-obvious techniques that you can’t get from the docs and instead have to learn from forums, programming guides, word-of-mouth, etc. Things like custom table cells and building the “full” and “lite” versions of your app with one Xcode project (hint: understand how “targets” work).
  • Oh Crap! I Forgot (or Never Learned) C! – In a way, this is the last gasp of a book that I wrote 100 pages of before it just ended up not happening. My thesis was that for all the developers who never learned C, or did but forgot, it’s a real bitch to be thrown into the world of pointers and malloc and life without objects, especially when the premier guide to the language was last updated during the Reagan administration, and contains no-longer-helpful analogies to Fortran and Pascal. The idea of the book was to be a C primer that you’d work through with the iPhone SDK, not in order to learn the iPhone APIs right away (we already wrote that book), but so that there would be a specific workbench, freely and easily available, for learning the C language (this is where K&R basically says “use cc on the command-line and if that doesn’t work, go ask a sysadmin.”). The session is going to survey the language from the point of view of scripters and other modern-day programmers, with particular attention to memory-management concerns, and idioms that are unique to C (things like setting up “context objects” for callbacks, because you don’t have closures).

Then, in April, I’ll be at 360iDev in San Jose (register with my special link), speaking on the topic of Core Audio. The talk, Core Audio: Don’t Be Afraid To Play It LOUD is one where I’m going to try to play up the fun factor a little more, and embrace the fact that Core Audio is a goddamned hard API to master. If you can do anything in Core Audio, you should feel awesome, and I’ve found that small successes in this API make you want to learn more. Oh, and bonus points if you catch the reference in the session title.

BTW, more news on the Core Audio front in an upcoming post…

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.

iPhone book touch-up

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.