New PhoneGap feature: ActionSheet support

A while back David Roe had sent me a patch for an implementation of the ActionSheet control for PhoneGap. He was using it in an application of his, and probably due to my plea for patches from the PhoneGap community at large, he submitted this little tid-bit to me. Since I’ve been making some pretty big strides toward implementing a unified callback and event dispatch mechanism within PhoneGap, I refactored a bit of it, and created a generic Dialog class for PhoneGap.

Currently only button dialogs are supported, but in the future I’d like to create a variety of dialogs that PhoneGap application developers can use; date pickers, scroll picker widgets, and so forth. The API is as simple as I could make it, while still keeping in mind that other platforms may not have a native control to accomplish these features.

Basically I was thinking “What would I want to put in an action sheet”, and I came up with:

  1. A list of buttons, maybe with different colours
  2. A date / time picker, maybe with a different start date
  3. A scroll-wheely value selector
  4. ….

Here’s the desired API I came up with for a very complex usage:

dialog.openButtonDialog(
{ label: “First button”, type: “default”, onClick: function() { … } },
{ label: “Second button’, type: “cancel”, onClick: function() { … } }
)

which would result in the following GAP command:

gap://Dialog.openButtonDialog/First button/Second button?type_0=default&type_1=cancel&onclick_0=…

and so on. With the event callback system I built, the onclick handlers will be given their own callback ID if they’ve been specified. Additionally, the following syntax is supported as well, for more simple uses:

dialog.openButtonDialog(
“First button”,
“Second button”,
{ onClick: function(index, label) { … } }
);

so you just give it a list of buttons, all shown as a default type, and the function would be called no matter what button was pressed.

This second form is necessary as you’d probably use it for the other types of action sheets:

dialog.openDatePicker(
new Date(‘2009-02-14’),
{ onClick: function(newDate) { … } }
);
dialog.openScrollPicker(
‘Value 1’,

‘Value 2’,
‘Value 3’,
{ onClick: function(value) { … } }
);

Those most recent examples aren’t implemented yet, but it shows how this particular class can be extended in the future. And they’re all potentially supported on other platforms as well.

Does all this sound reasonable? Please leave comments if you have any thoughts on the matter.

Check out the documentation of the Objective-C Dialog implementation, and the JavaScript Dialog interface to it. If you want to play with this before I push it into the official branch, you can try it in my experimental UIControls branch.

Minor PhoneGap Alert updates

Last night, and on my commute in to work this morning, I made some updates to my UIControls branch on Github, largely around adding callback and event support to the Alert notification code.

I’ve been working on providing more feedback into JavaScript from the commands run inside of Objective-C.  So far PhoneGap has been largely one-way, you push actions into PhoneGap and maybe you get some sort of response out in some general sort of way, but most of it has involved polling mechanisms.  Well the DOM and JavaScript in general has a native facility for dispatching ad-hoc events.  And it’s super easy to call from Objective-C, if only a little bit verbose:
[webView stringByEvaluatingJavaScriptFromString:
@”(function(){ “
“var e = document.createEvent(‘Events’); “
“e.initEvent(‘alertClosed’, ‘false’, ‘false’); “
“e.buttonIndex = %d; “
“document.dispatchEvent(e); “
“})()”,
buttonIndex
];
It looks a little complicated, but it really isn’t.  It’s calling some code in an anonymous function closure so this doesn’t leak any references or variables into the global scope, creates a DOM event, sets it with a custom event name, adds some arbitrary properties to it, and dispatches it against the document element.
This same pattern applies everywhere some event occurs, and something in the JavaScript side of the gap:// barrier might be interested in it.  Listening to this event is as simple as doing:
document.addEventListener(‘alertClosed’, function(e) {
debug.log(“Alert box with button ” + e.buttonIndex);
}, false);
Pretty simple, and super flexible.  If you’re interested in an event, you bind an event listener to it.  If you’re not, you simply ignore it.  I’m pretty excited about this design pattern since it reduces the complexity of having to loop and wait for something that might never happen.
Update: Oh, I almost forgot.  The main reason for doing all this is I added the capability to add a second button to the Alert popup.  So you can do “OK / Cancel”, or whatever else you want.  But doing so necessitated adding callback support so you could tell not only when the user closed the alert, but so you could tell which button they pressed.

Long weekend at the Spiller Estate B&B

My wife and I had to go up to the interior of BC to clean out an old storage unit of the junk I don’t need anymore, and close it up for good.  Luckily for us this went faster than we’d expected.  Instead of 1-2 days of cleaning and carting stuff off to the thrift store, it was 2 hours of sorting through what I wanted, and left the rest to be picked up by the thrift store on Monday.

This left us a 3-day weekend with no obligations, and no plans.  Since we were a short drive from a ton of family, as well as some of the nicest vacation spots in a variety of small towns nearby, we had two choices: Either try to cram as much visiting into one weekend as we possibly could and couch-surf, or play tourists for a weekend and enjoy ourselves.
So after a nice lunch where we did some research on our iPhones, we ended up driving a couple hours to Penticton, a fairly small town in the heart of British Columbia’s winery region.  Googling for “Winery B&B” for our area turned up a few results, but the one that caught our eye was Spiller’s Corner B&B & Winery.  A nice heritage B&B, right on a vineyard, and close to several wineries.  We called and booked the room before we got the bill for lunch, hopped in the car, and made our way over.
The weather was fantastic, and since we arrived fairly early, we were greeted with a free wine tasting of the wines made right on that vineyard.  We discovered that it’s a “No-Host” B&B which I’d never heard of before.  Someone comes in the afternoon to clean the rooms, and someone else comes in the morning to cook breakfast, but beyond that the guests have the whole house and grounds to themselves (with the exception of the separate building used for wine tastings, which is open from 11:00am – 5:00pm).
We spent our days there just relaxing and enjoying ourselves; wandering to the different wineries and bought a few bottles of the tastier ones we found; we sat in the sun tanning, reading, or playing cards; we even bought some groceries from the local Safeway in town and barbecued some salmon for dinner.  And in the farm across the street there was a mare and newborn foal that were absolutely adorable.  After the breakfast the cook made for us one morning, we took our morning coffee and walked across the street and visited the baby horse that adorably found everything around it new and adventurous.
All in all it was exactly what the both of us needed.  A break from the fast-paced city life, a relaxing road trip through the country, listening to the birds, the wind, tasting wine and catching some sun.  With the exception of lunch at a restaurant one afternoon with terrible service of epic proportions – I’d advise against eating at The Hooded Merganser, unless you feel you have better luck than we did – our trip had the most relaxation packed into a 3 day weekend of any other I’ve had.
I consider myself lucky to live in such a richly diverse province, and to have such a wonderful wife to share it with.

I

It’s too bad I can’t use it at work, but HTTP::Engine rocks my world. It does “The Right Thing ™” for negotiating HTTP requests and their content in a wonderfully transport-agnostic way. That means that if you’re running in mod_perl, FastCGI, plain ‘ol CGI, or even running as a stand-alone development-mode daemon, it will just work.

At my work, we’re building new user interface components to our email security appliance product, and for the first time we’re building a web interface that will be used by a potentially high number of public users. Up until now, all of our target users were internal to our customers, meaning system administrators and a small number of workgroup users. Now however, their customers and the recipients of potentially a high number of emails will be able to interact with our appliance. This means we have to scale a lot higher than we have needed to in the past, all with limited hardware and memory.

So for the first time in eons, since before I started working with mod_perl back in the late 90’s, I’m having to parse HTTP POST data, including file uploads, by hand. Catalyst does it really well, but unfortunately the list of dependancies it has makes it prohibitive to pull in to our build. That’s when I discovered HTTP::Engine. It looks great, it does what Catalyst does for managing HTTP requests in a flexible and extensible way, gives all sorts of great developer hooks to access internal information about the request, and it doesn’t require that you buy in to Catalyst’s way of doing things (of course, when I’m at home, I develop my apps using Catalyst).

Sadly though, HTTP::Engine’s list of dependancies is almost as long as Catalyst’s. Normally this wouldn’t be a problem, but given our restricted memory limits, I just can’t justify using it. Which really blows, because as CPAN modules go, this one is pretty darned sexy!

I’ve decided to simply write a sub-class of HTTP::Request that does what we need, and tie it in to the Nginx 3rd-party module for file uploads.

Maybe someday I’ll be able to use Moose, Catalyst and HTTP::Engine on our appliances deployed in customer locations around the world, but for now I’ll just have to roll my own.

Open letter to Apple iPhone Developer Support

I’m a big fan of all things Apple, and as you can tell from my past blog posts I’m a big fan of iPhone development. I’ve even dusted off my aging C skills, and have learned to love Objective-C. The one thing I haven’t learned to love, like all other iPhone app developers, is their application release process, and the seemingly arbitrary app store acceptance department.

Don’t get me wrong, I think how Apple fiercely guards the App Store to prevent bad, buggy, or offensive material from getting on there is a great thing. Some of my mother-in-law’s students in the class she works in have iPhones or iPod Touches, and these little 10-year-olds love the little apps I’ve put out. They’re fun, light-hearted, and they get a lot of enjoyment out of these and other apps. It’s reassuring to know that if I install an app, it won’t crash my phone (too badly) or that a child won’t be offended by them (too much).

However, the level of detail they give in their rejections seem both arbitrary and unnecessary. You develop an app and have to throw it over the fence to Apple, after which you wait with no level of detail as to the status, or success, or any sense of progress through whatever queue they have. Finally, you more often than not get a big fat rejection letter that gives you no detail as to why your app didn’t meet their secret criteria.

In my experience so far, this has actually been a virtue. My apps actually did have minor race conditions, or problems on specific platform versions. So, fair enough, I fix my changes and submit them in. But recently, the framework I work with and have been helping develop seems to be under fire from Apple for no apparent reason. And it’s the contradictory nature of their message that is what gets under my skin.

In this long thread on the PhoneGap mailing list, a number of developers writing their applications under PhoneGap have been given rejection letters saying something like:

Upon review of your application, cannot be posted to the
App Store due to the usage of private API. Usage of such non-public
API, as outlined in the iPhone SDK Agreement section 3.3.2 is
prohibited:

” An Application may not itself install or launch other executable
code by any means, including without limitation through use of a plug-
in architecture, calling other frameworks, other APIs or otherwise.
No interpreted code may be downloaded and used in an Application
except for code that is interpreted and run by Apple’s Published APIs
and built-in interpreter(s).

The PhoneGap API implemented in your application is an external
framework.

For those new to PhoneGap, it’s a project that gives you an XCode project directory with Obj-C classes pre-made that allow you to develop iPhone apps in HTML / JavaScript. It wraps up several of the iPhone’s native controls and exposes those capabilities to JavaScript. In this way, an application developer could write their app in a hybrid of native Objective-C code and the iPhone’s own native browser control, the UIWebView.

It seems though that Apple constitues the use of their own controls as a 3rd-party library. Apple’s own developer documentation includes code sample projects, much like PhoneGap’s, intended to get a developer started on using their SDK. Is it incorrect to assume that others can do the same?

Additionally, there are 3rd-party companies such as AdMob, Medialets, and a number of others that provide ads, application tracking, and other resources to iPhone developers. Their code is given to you as pre-compiled libraries, that are most certainly not included on the iPhone when you pull it out of its shrink-wrap. So how is it that all these apps are released to the App Store with these 3rd-party libraries linked in, and an app framework whose source code is freely available and uses officially documented features can’t be?

I decided to put an end to the speculation, and wrote a letter to Apple’s developer support on this matter. I’ll give it a chance to percolate through their support department, and if I don’t hear an answer back via email, I plan to call their support department until I can get an answer.

For future reference, and in the interests of keeping the discussion open, here’s a copy of the letter I sent in to Apple.

From: Michael Nachbaur
Date: May 17, 2009 6:04:28 PM PDT (CA)
To: idp-dts@apple.com
Subject: Library classification clarification

Hello, I’m an iPhone software developer, and one of the core developers of the PhoneGap project. A number of users of PhoneGap – a set of Objective-C classes aimed at leveraging the UIWebView to access iPhone-supported hardware features – have reported that their apps have been rejected from the App Store because they supposedly use a “3rd-Party Library”. I wanted to get some clarification about this, as this is not only untrue, it is completely at odds with the goals of our project.

PhoneGap only uses officially-supported features of the iPhone, as documented within XCode’s iPhone SDK documentation. We even make sure that we don’t even use deprecated features of the iPhone, as we want to ensure 100% compatibility. All the software we use is exposed natively by the iPhone, and is in use in many other apps on the App Store.

So I wanted to get an official clarification from Apple as to why these apps are being rejected, and what, if anything, we as the maintainers and developers of the PhoneGap project can do to rectify the situation. We are trying to empower developers with quality starting-blocks for developing their own applications, much as the Apple sample applications do.

So please, if there’s anything we’re doing wrong, we would more than happily change our code to accommodate Apple’s policies. As far as we can tell, we support Apple’s licenses even better than other apps on the App Store, because we don’t import 3rd-party libraries such as AdMob, Medialets, or any of the other ad and tracking libraries that are obviously featured on almost every app in the App Store. And these are quite plainly 3rd-party libraries, and as such should they not be even more restricted than we are?

As a community, of both software developers and of business-people, we are very anxious to hear back from you, and would like to discuss this with someone at Apple to get an official answer, instead of the conversation being one-sided and filled with speculation.

Thank you for your time, and I look forward to hearing back from someone soon.

What are your thoughts on the matter? Can anyone reading this find some reason why Apple would target PhoneGap?

Update: I got an update on this problem from Steve, one of the app reviewers, over at Apple.

My blog and I are joining the Iron Man competition!

It’s probably not the kind of Iron Man competition that you’re used to hearing about. This one is a challenge to blog at least once per week, every week, about Perl and Perl-related technologies.

My buddy Matt Trout, co-founder of Shadowcat Systems, creator of DBIx::Class, a core contributor to Catalyst, as well as all sorts of other great Perl goodies, is starting a blogging contest to try to get the word out that Perl is still alive and well, and perhaps try and overthrow the public perception that Perl is use old-school “use CGI;”. And he’s also a hilarious public speaker too.

I’m going to be taking part from this blog. I love Perl, and have been writing software in it for nearly 15 years. The problem with a lot of people like myself is that we already know that Perl is great, and is used throughout the world to solve mission-critical problems on a daily basis. But we tend to forget that unlike Java, .Net, and other crappy newcomers, Perl doesn’t have a marketing department trying to convince people that their language doesn’t suck. Anyone who’s had to deal with Java CLASSPATH problems, .Net’s problems like – well, that it’s .Net, for starters – knows that just because there’s industry buzz about something doesn’t mean it’s lacking in the “SUCK” department.

Please check out http://www.enlightenedperl.org/ironman.html and join in. Or alternately you can email ironman@shadowcat.co.uk if you want to contribute. At the very least, I suggest you follow the Enlightened Perl Iron Man blog. If you’re following this one, at the very least you’ll be following a small part of it.

So much to do, so little time

I think work wrecked my brain a bit today. I have so many ideas running through my head, and just not enough time to catch up to them all. It happens to all of us from time to time, but the thing is I have so much fun with work, that it’s hard to moderate myself. I think I’m going to head home, listen to some Bob Marley, and when I meet up with my wife and her parents at the pub, I’m going to watch the hockey game, drink some beer, and enjoy my evening without technology.

That being said, I’ve recently made some great updates to PhoneGap, and have added some docs. Check out the generated JavaScript documentation and iPhone documentation from my personal branch at Github. I’m sure we’ll get the docs pushed up to the main PhoneGap website soon.