Tag Archives: Code refactoring

New PhoneGap feature: ActionSheet support

29 May

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.

I <3 HTTP::Engine

22 May

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.

PhoneGap UIControls ready to go

24 Apr

I’ve merged the results of my UIControls branch on github into my master branch. I think my little experiment went well, and I’d love to get feedback from people on how this new API works for you. I still have some great plans for it, but before I get ahead of myself, let me cover what I’ve done:

Changes:
• Refactored the command call API to allow for a richer set of arguments to PhoneGap commands
• Moved some commands around to more appropriate classes (e.g. Alert and Vibrate both moved to Notifications)
• Reorganized the XCode project so commands are clearly separated from PhoneGap infrastructure
• Renamed the Settings.plist PhoneGap config file to PhoneGap.plist, and created a new Settings.plist file that contains custom application-specific configuration.
• Made all PhoneGap commands inherit from a common base-class that auto-loads its own Dictionary of configuration from the main PhoneGap.plist file.
• Added UIControls command class that exposes tab bars and toolbars to JavaScript.
• Updated the demo to show off tabs and toolbars

All that looks like a big change, but almost all of it was infrastructure changes that were necessary to get UIControls to work. Previously, all PhoneGap commands were class method calls, meaning it was very difficult to maintain state between command calls. Now that commands are actually called on an instance of a given command class, it’s easier to maintain state. So when a tab bar is created, multiple calls can be made, each to construct different aspects of the UI. Without all this, the following example would have been much more complicated:

uicontrols.createTabBarItem(“toprated”, “Top Rated”, “tabButton:TopRated”);
uicontrols.createTabBarItem(“recents”, “Recents”, “tabButton:Recents”);
uicontrols.createTabBarItem(“more”, “More”, “tabButton:More”);
uicontrols.showTabBar();
uicontrols.showTabBarItems(“toprated”, “recents”, “history”, “more”);

The changes to the PhoneGap configuration were necessary because now, when a PhoneGapCommand subclass is constructed, it will look in the PhoneGap.plist configuration file to see if there’s anything pertaining to it. So if there’s a key in the dictionary with the same name as the class being constructed, it will use that as a local configuration dictionary influencing just that one class. That way, if you don’t use a feature of PhoneGap, or you have to configure a lot of options for a single type of command, these options won’t be cluttered alongside the standard global PhoneGap settings.

I created Settings.plist because of experiences I had in an application I’ve been creating in order to test my new UIControls branch changes. I found that I wanted to set compile-time options (for instance a “lite_mode” boolean) that influenced the way my app runs, without having to change HTML or JavaScript code every time. So instead what I have is a configuration plist file that is used exclusively in JavaScript for my application. I’ve added the excellent SB-JSON framework in to the PhoneGap project, and use that to pass these settings into the JavaScript application at start-up time. So all you have to do is read Settings.lite_mode, for instance, in order to read properties set in your plist file.

Oh, and finally, I’ve moved the JavaScript documentation to javascript/docs, since they were practically impossible for new users to find in the past. I’ve been working on creating DoxyGen docs of the PhoneGap code for use in XCode’s documentation browser, but I haven’t gotten far enough there to actually check anything in besides comments. I found instructions on how to generate XCode docsets from DoxyGen, but I haven’t gotten it working just yet.

So please, try out my PhoneGap updates and let me know what you think of it. I think the integration of the JSON framework will make things like Contacts much simpler to implement.

Before I go though, I want to give a little “wish list” of features I’m planning on adding in the near future. I’ll get to them whenever I can, since my own app development takes priority of course.

• Toolbar buttons
• Tabbar show/hide animations
• File support (read / write local files)
• Camera and photo library support (POST to a server, save to a local file, etc)
• Native “Flip / Slide” transitions (no more having to mimic them in CSS)
• 3rd party API integration, like AdMob and Medialets (I already have both of these done, but I’m in discussions with both companies to determine if the terms of their license allows me to redistribute it)

Native UI Controls in PhoneGap coming along nicely

20 Apr

They say a picture’s worth a thousand words. Frankly I think inflation has really taken its toll on the cost of words, but nevertheless here’s a quick view of what’s been going on in my UIControls PhoneGap branch on Github.

In that screenshot the UIWebView frame, aka the actual webpage content of the app itself, was automatically resized so the toolbar and tabbar has room to be shown. And yes, those are native widgets for the toolbar and tabbar.

Currently the toolbar doesn’t support showing of any buttons, but the tab bar is fully functional. What it doesn’t support (or at least, it’s not tested at all yet) is using custom images in the tab bar items. But that’ll happen shortly.

Next on my roadmap before this feature can be pulled into the main PhoneGap branch is adding buttons to the toolbar, and animating the tool and tab bars when they’re shown. Currently they just pop into view. I’d like it to be possible for it to slide or fade in instead.

Check back here, or subscribe to my feed, to stay tuned on my additions to PhoneGap.

More changes coming to the iPhone branch of PhoneGap

14 Apr

Last Thursday I went down to Nitobi after work for a couple of beer and a chat about PhoneGap before I had to go give a couple lightning talks at the Vancouver.pm Perl Mongers group. My hour chat with a couple of the Nitobi crew turned into over 2 hours, making me late for Vancouver.pm, but I think the effort was worth it. First off, they have good taste in beer. Second, I had an opportunity to do a little show-and-tell of my PhoneGap branch on Github. Not only did they like my changes, but I told them what my roadmap of features for PhoneGap includes, and I think they’re on board.

So to that end, I’ve started with the changes I said I’d work on. I cleaned up the console / debug logging code I recently developed, to allow for the main meat of what I’m working on: A flexible API for dynamically creating native UI widgets such as toolbars and tabbars. But before I can do that, I wanted to refactor the calling method that PhoneGap uses.

In order to make PhoneGap commands stateful, and to be able to pass a richer set of instructions to those commands, I’m refactoring the call API to look like so:

gap://Class.Method/arrayEl1/arrayEl2/…?dictKey=dictVal&…

The first command to a given class will construct it, passing the reference to our UIWebView to it as a constructor argument. All command classes now inherit from a new class called PhoneGapCommand, which stores the webview as an instance property.

So then the first, and all subsequent commands, are invoked with two parameters:

  1. (NSMutableArray*)arguments
    This is a list of arguments passed on the path component of the URI. They’re URL decoded for you, so any %-encoded characters will be expanded for you.
  2. (NSMutableDictionary*)options
    This gives you a dictionary / hash of the query string component of the URI. This is always passed, but if a command doesn’t supply a query string, the dictionary will be empty.

Calls from within JavaScript will take the first hash (object in JavaScript notation) it sees and will use that as the query string. All other arguments need to be strings / numbers, or else they’ll be dropped. This means you can pass your options hash as the first, or even the last argument in a PhoneGap.exec call.

The bulk of this code is almost done, so I’m excited to hear what people think about this. I’m just getting the existing commands working with this model before pushing back out to Git, at which point I’ll continue on and implement native UI toolbars / tabbars.

Update: I’ve updated my branch on Github with my latest changes. I think they’re great, but there are still some changes that need to be made. I got most of the API working, except for contacts. But since they weren’t working so hot to begin with, and someone else has stepped up to the plate to improve Contacts, I’ll let him merge his updates in to my new code. Next up: native UI controls!

When to refactor, and when to slash and burn it

5 Mar

Slash and burning in BrazilImage via Wikipedia

I’ve said it many times before, but as programmers we tend to spendthe bulk of our time working with broken software, and we try to endeavor to make it less broken. When it’s not broken enough to keep people from using it on a day-to-day basis, you release your software. Some places do this better than others, and at my day job we’re really good at it. We are very dilligent about our software releases, we have tons of unit tests, system test automation, a nightly test “dashboard” that shows us how well our tests passed, and so forth.

But even though we rock at our tests, regardless, there are things that we would like to have work better than they do now. That’s what differentiates players in the marketplace. You can have a product that works, but if it doesn’t work as well as your competitors, or it doesn’t solve problems your customers have, then you might as well not be in the game at all.

When to refactor?

So when do you refactor your code? When do you decide that what you have could be improved and you should change the way it functions? Usually this happens as a customer suggestion, request, or demand. Product management may decide that a feature should be added, or an existing feature should be improved. That’s when we programmers dive in, and change what’s there while trying not to break it. Hopefully, in the process, you’ll come out the other side with better test coverage, and will have or improved the code around the areas you were working.

Sometimes this isn’t enough though. What if fundamental design decisions about the section of code you’re working on isn’t up to the task of the refactor? Or perhaps the code has just outlived its usefulness, and needs to be thrown away with the bath water?

When to throw it all away?

Inevitably we just need to set fire to the whole thing and write something new in its place. Slash & burn. This is the decision I’m faced with now. We have a feature that hardly any of our customers use. Some people say it’s because other sections of our product work so well, that this troubleshooting interface just isn’t all that necessary after our customers gain confidence in how well our product works.

I personally think people don’t use it because, well, it’s slow and ugly. It works perfectly well, and all of our tests confirm that it works exactly as designed. But I think the design itself is flawed. Plus, there’s tons of additional features we could develop on top of this code if we re-wrote it.

Why haven’t people asked for this rewrite though? Well, because they frankly haven’t thought of it. As developers, we know more about what is possible and what is there, and it’s our duty to let our users and product management know what could be made possible.

I just had a long chat / discussion / argument with one of our product managers. In the end, I had him excited about what we could build…but it took a lot of convincing.

Frankly, I’m not happy if I just build the features fed to me through the management pipeline. I’m an inventor at heart, and I’m just not satisfied if I’m doing just I’m told. If you give me an established product, with a large customer base, and a sales force that is poised to strike when they get a feature with which they can hit the pavement running, I’ll come up with a ton of ideas to make that happen.

I suppose what I’m saying is it’s not always the right decision to do the obviously safe approach. Sometimes you just have to set fire to something that, on the surface, looks healthy and “good enough”. Learn from what you have, and replace it with something 10x better. From the ashes, hopefully you’ll build something that nobody else in the market place has. From that your company gets a competitive advantage, and as an inventor, I get personal satisfaction that I’m making our product better, and am earning my keep.