I’m diving in to PhoneGap and ObjC, finally
I’ve managed to avoid C and C++ programming for most of my career of 15 years. I’ve dabbled, and then I’ve run back to my favourite languages. Besides, as a web developer there hasn’t often been a need for me to delve that deeply into the machine.
Now that I’m developing iPhone applications, that’s all changing. I’m using PhoneGap to develop my apps, but some of it was buggy, and others just didn’t do what I needed to do. I’m integrating the AdMob and Medialets APIs into my application, which means Objective-C programming. After picking my way around, and continuing on the work my friend Scott McWhirter developed, I think I’ve come up with something that not only works well, but helped me to learn and love Objective-C.
Why I started playing in ObjC
Scott had fixed a long-standing bug in PhoneGap which happened when multiple PhoneGap commands were made within the same event loop in JavaScript. After he figured out how it worked, and how to fix it, that showed me just how PhoneGap works. To be honest, this is the first time I really understood how it does what it does.
As I integrated Medialets in, I discovered that it has the capabillity of sending custom tracking events so developers of iPhone applications can not only analyze when and where people are using their application, but what they’re doing while they’re using it. I wanted to use this to see how effective parts of my first application are, so I can learn what to change in the future.
Sadly, since most of my application is written in HTML/JavaScript, there was no direct way I could call out to the Medialets ObjC API directly from my JavaScript events. I couldn’t do copy/paste code any longer, I needed to extend PhoneGap to add my own commands to it.
Why I Refactored PhoneGap
The way PhoneGap commands worked in the past is it would set “document.location” to an address that looks like “gap:command_name”. If any arguments were supplied, they’d be added on the end separated by colons. But since that is a relative path, it would be added on to the end of your application’s filename. So, since HTML files are loaded locally from within the app’s directory on the iPhone, a GAP command would look like:
file:///…MyApp.app/www/gap:command_name
So figuring out if a command is being called involved a lot of hackery around stripping out the gap: portion of the URL. Since this was difficult to deal with, and meant there was a lot of re-used code, not to mention commands couldn’t contain colons, or any characters that could potentially be URL encoded, I refactored GAP commands to be their own absolute paths, like so:
gap://command_name/arg1/arg2/arg3…
When this location is set, a callback within the Objective-C code (called shouldStartLoadWithRequest
) checks the address and determines if the request is a real request and should be allowed to continue, or if it should process the command as a GAP command and disallow the link from being loaded.
So these URLs are never loaded outside of the phone. Once PhoneGap sees a gap:// URL, it parses it as a command request and never attempts to fetch the resource. The benefit of this is that the iPhone’s NSURLRequest
and URL
objects automatically decode the URI parts and escapes any %20-style encodings. It also is a lot simpler to find the name of the command to run, since all it has to do is fetch the URL’s hostname property.
I’ve pushed this to my own fork of PhoneGap up on GitHub if you want to try it yourself. Hopefully these changes get pulled down into the main branch of PhoneGap.
Next Steps
Realizing how easy it is to write Objective-C, I’m planning additional changes in the future. First, I would like to refactor the gap.js JavaScript code to use Joose, a light-weight meta-object class framework. This way, instead of calling out to a separate class like Device.exec to call out to PhoneGap, you could inherit from PhoneGap and create a class-based application that extends it instead.
I would also like to make the commands within PhoneGap pluggable, so one doesn’t have to extend one giant if/else block. Instead, it would be nice to have a pluggable framework where you can add commands by inheriting separate classes. I don’t know enough to do this yet, so unless someone else does something similar, it will have to wait.
Certainly I’ve learned quite a bit, and had a great time doing it! I’m really enjoying PhoneGap and iPhone development, and I’m looking forward to adding more as time goes on.