Archive

Archive for the ‘PhoneGap’ Category

HelloPhoneGap1.0 Project Updated for PhoneGap 1.3.0

January 10, 2012 1 comment

I just updated the HelloPhoneGap1.0 out on github so that it works with PhoneGap 1.3.0, iOS5, and Xcode4 with the new project structure. Let me know if you see any issues…

Where to get the Code? (PhoneGap 1.3.0 Required)

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

PS. MAKE SURE YOU INSTALL PHONEGAP!

About these ads

Debugging iOS Apps Using Safari Web Inspector

November 22, 2011 13 comments

 

Enabling Remote Debugging using Safari Web Inspector for iOS

 
Wow! What a day! While researching debugging techniques for an upcoming training class I am teaching on PhoneGap, I stumbled over a couple of blogs about a private API in iOS5 that will allow you to debug your UIWebViews right inside desktop Safari using the Web Inspector!

It’s so simple! All you have to do is add this to your AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Uncomment to enable remote debugging
    [NSClassFromString(@"WebView") _enableRemoteInspector];
....

Then use Safari to access http://localhost:9999 and you are debugging with Web Inspector and have full debugging capabilities including breakpoints and DOM manipulation among other things!

It only works on iOS5 and it only works when running in the simulator BUT it’s an awesome upgrade from Weinre if you are building an iOS app.

Kudos to @atnan for finding it!!

References

See this blog by @atnan for more info and tips for enabling debugging for Mobile Safari in iOS.

Also see this blog by @firt for a nice tool – iWebInspector – that makes enabling debugging a little easier.

HelloPhoneGap1.0 Project updated for PhoneGap 1.1.0

October 28, 2011 34 comments

I just updated the HelloPhoneGap1.0 out on github so that it works with PhoneGap 1.1.0, iOS5, and Xcode4 with the new project structure. Let me know if you see any issues…

Where to get the Code? (PhoneGap 1.1.0 Required)

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

PS. MAKE SURE YOU INSTALL PHONEGAP!

HelloPhoneGap1.0 Project Created – Compatible with iOS 5, PhoneGap 1.0, and XCode4

August 17, 2011 24 comments

I just created a new project HelloPhoneGap1.0 with the code out on github so that it works with PhoneGap 1.0, iOS5, and Xcode4 with the new project structure. Let me know if you see any issues…

Look for an upcoming post that details the changes in the project structure and how to go about extending and updating the PhoneGap library in the new project.

Where to get the Code? (PhoneGap 1.0 Required)

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

HelloPhoneGap Project Updated for PhoneGap 0.9.6

June 29, 2011 5 comments

 
Just updated the code out on github so that it works with the 0.9.6 PhoneGap. Let me know if you see any issues…
 

Where to get the Code? (PhoneGap 0.9.6 Required)

 
You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap

Debugging PhoneGap & JavaScript

 

How to Debug PhoneGap and JavaScript

 
If you have ever written any JavaScript and tried to get it working on a mobile phone then spent hours banging your head against the wall because it just doesn’t work, this post is just for you. Today, we will be looking at ways to troubleshoot, diagnose, and debug your mobile web project. While I will be focusing on debugging interactions with PhoneGap, the strategies outlined here are really applicable to any JavaScript or mobile web project. Since I have been focusing mainly on iOS development, this post will discuss the debugging of JavaScript and PhoneGap within an iPhone application but the same ideas should work for Android and other platforms as well.

Pre-requisites:

 

Where to get the Code?

 
To demonstrate the troubleshooting and debugging strategies outlined below, I will using the HelloPhoneGap project that I created for my PhoneGap Tutorial Series.

If you are using PhoneGap 0.9.6

You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap

If you are using PhoneGap 1.0

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

 

Common Problems

 
Over the course of putting together various applications, I have undoubtedly made many mistakes and spent many hours trying to figure out what went wrong. I have misplaced quotes, brackets, mistyped JSON and struggled with writing well-formed JavaScript that “looked right to me.” I have misspelled method names, overwritten global variables, and forgotten to “install” my PhoneGap plugins. I have also tried to use PhoneGap methods in inappropriate locations and sometimes couldn’t figure out why the feature works on my phone but doesn’t work in the simulator. After all of those hours of trial and error, I would like to share some of the strategies that I used to help figure out my mistakes.

Tip #1 – JavaScript Syntax Validation

So you wrote some JavaScript, added it to your page, wrapped it in PhoneGap, launched it in the simulator, waited patiently for a bit, clicked your mouse, and nothing happened. Huh? No errors, nothing. What could possibly be the problem? Well, there are many possibilities but the most likely is that you have malformed script. When running in a webView the JavaScript engine isn’t very forgiving when it comes to mistakes and it doesn’t bother to tell you about them either. Your page may render and the html may look pretty but if you missed a bracket somewhere you won’t get a nice little red icon or a full suite of debugging tools to help you figure it out. When running on the simulator or even on your phone, you do not have the same tools that you would have if you were running in a desktop browser. So, what to do?

Depending on your development tools, you may or may not have decent support for JavaScript. If you are writing an iPhone app, you are most likely using XCode for your development. You probably have nice color coding and some not-so-smart code completion but if you want to write well formed JavaScript it doesn’t really help all that much. What you can do is use a tool called JSLint to validate your code and point out those not so obvious syntactical errors.

There are a couple of ways run JSLint:

  • JSLint.com — this website allows you to cut and paste your code into a form and it will validate your code
  • JSHint.com — some people may feel that JSLint.com is a little too restrictive, if you are in this camp then you could also try using JSHint.com.
  • SproutCore Bundle for TextMate — the nice folks at SproutCore have built a free bundle for TextMate that you can use to run JSLint right from within TextMate. Just install the bundle and run the “Validate Syntax” command. The bundle uses the JavaScriptCore.framework library from Apple to run the JSLint scripts. The bundle can also “beautify” your script which is pretty handy as well.

JSLint can handle standalone script files as well as html files with script tags but there are a few limitations:

  • All tag names must be in lower case.
  • All tags that can take a close tag (such as <p></p>) must have a close tag.
  • All tags are correctly nested.
  • The entity &lt; must be used for literal ‘<’.

If you use TextMate, you can make sure that your HTML code is compliant prior to running JSLint by running the “HTML Tidy” command first.

Tip #2 – JavaScript and HTML inspection

After making sure that you have well-formed JavaScript and you are convinced that can’t possibly be the problem, the next step is … debug! Oh, wait….there isn’t really a JavaScript debugger available for the iPhone webView! Now what?? Well, Patrick Mueller has released an awesome tool – Weinre – that will allow you to interact with your webView remotely. You can inspect and edit the DOM, CSS, view log messages and even run JavaScript on the fly. It’s the closest thing to an actual debugger for the mobile web that I have seen to date and while it takes a bit of effort to get set up and running, it’s well worth it.

You can download Weinre from github and detailed instructions for installing and using it can be found here. The Weinre server app provides the tools to interact with client WebKit browsers. In order to make this happen you must set up and run the Weinre server and then add a script tag to each page of your web app that you wish to inspect and interact with.

The client side Weinre script uses AJAX to establish a connection and communicate with the Weinre server and allow the on demand inspection and editing of the DOM, CSS, and JavaScript. Jonathan Stark, author of Building Apps with HTML, CSS, and JavaScript, has put together a nice screen cast of Weinre in action – you can view it here.

Once everything is up and running, you can use the Weinre tools to check that PhoneGap has been initialized and that you are using the correct PhoneGap API with the proper parameters. You can also inspect the DOM and your CSS, dynamically make changes and see how they may impacts your mobile site in real time.

Now, just so you don’t make the same mistake that I did — don’t forget to remove the Weinre script from your pages when your are done debugging your pages. I lost hours one day when my app was suddenly hanging until I realized that I had forgotten to remove the script and my app was timing out looking for the Weinre server that no longer existed.

Tip #3 – JavaScript Debugging in the Desktop

Now that you have verified that your script is well-formed and had a chance to inspect your site with Weinre, you may still have a problem and really only debugging will do. Well, there is always the poor man’s debugger: “alert(“made it here!”);”, but that’s not very useful if you are using a lot of JavaScript or are dependent upon third-party libraries like JQTouch or SenchaTouch to do most of the heavy lifting for you. So, what else is there to do? Debug your app using your favorite browser based tool set of course! (I used Safari and Web Inspector….)

I know what you are thinking! It’s a mobile browser! How am I supposed to debug it! The answer is that you aren’t. You are going to debug your HTML, CSS, and JavaScript app in a desktop browser. The trick is to remove any dependencies that you have on PhoneGap before debugging. There are a couple of ways to go about this but the basic gist is to use some sort of mechanism to replace the PhoneGap calls with something else – a PhoneGap “shim” if you will. Several people have made their versions available out on github with varying degrees of capability.

PhoneGap “shims”

  • https://gist.github.com/476358 — this snippet comes to us from Jesse MacFayden (@purplecabbage) and allows calls to be made to PhoneGap without causing JavaScript errors. With a little editing I was able to show an alert with the details of the phonegap command that would have been executed. To see the edited version – open the PhoneGapShim1.js in HelloPhoneGap project. I tested it out using the camera.html and the geolocation.html files in Safari.
  • https://github.com/alunny/stopgap — this snippet basically just wipes out the PhoneGap object and fires the ondeviceready event. This might be useful if you don’t rely on PhoneGap for all that much beyond display and don’t care about testing your interaction with the PhoneGap device APIs.

Overall, the shims that I found were a little lacking but there is nothing stopping you from rolling your own. With a little more manipulation, the snippet from @purplecabbage could be easily extended to provide a more robust framework to mock out PhoneGap and simulate device interactions.

That’s all for now! Stay tuned for future posts and don’t forget to check out my PhoneGap Tutorial Series!

PhoneGap Tutorial Series – #6 Writing Your Own Plugin

April 15, 2011 9 comments

 

How To Create Your Own PhoneGap Plugin

 
Today’s topic is about creating your own PhoneGap plugin for iOS development. PhoneGap provides a whole array of built in features to access all sorts of things on a device but what if you want to do something that is not already supported and has not already been provided by a third-party plugin? Stop fretting, just like adding a third-party plugin, if you can write a little Objective-C then you can add your own plugin to PhoneGap as well.

In order to create your own plugin you will have to be somewhat familiar with JavaScript as well as Objective-C. This tutorial assumes that you are familiar with JavaScript, Objective-C, the iOS SDK, and the XCode development environment. This post also assumes that you are familiar with the PhoneGap project structure and the PhoneGap plugin architecture, so you may want to peruse some of my earlier posts before continuing on if you haven’t already.
 

So…You Need a Plugin to do XYZ … Now What?

The process of creating your own PhoneGap plugin is relatively simple and depending on the complexity of the plugin, takes just minutes. I have provided two examples that have different types of behavior.

1. ActivityIndicatorCommand – The first plugin is very simple and provides a mechanism for showing an activity indicator with a message, updating that message, and hiding the indicator. This plugin could be useful if you are loading a webpage or you have a long running server side post and want to prevent user interaction. This plugin does not use the onSuccess and onFailure method callback pattern since it’s a very simple user interface addition.

2. iPodCommand – The second plugin provides a mechanism to interact with the iPod music library, select a song, and play that song. This plugin does use the OnSuccess and onFailure pattern to notify the JS to show details of the selected song and allow the user to start/pause playing of the song. You must be running on a device for this plugin to work as the simulator does not have an iPod library.

Note: the iPod plugin is a VERY simple implementation and is not meant for production use as it does not take into consideration audio route changes (like plugging in headphones or a phone call coming in etc) among other things. The example is given in order to demonstrate a successCallback for PhoneGap not as a tutorial for how to write an audio streaming application.

See this link for more information on iPod programming.

To get started you need to create several files:

  1. Create a new directory in your ${PROJECT_DIR}/plugins directory (ex: ActivityIndicator)
  2. Create a JavaScript file within that new directory (ex: ActivityIndicator.js)
  3. Create a new Objective-C class that extends the PhoneGapCommand class, also in the new directory (Ex: ActivityIndicatorCommand.h & ActivityIndicatorCommand.m)

Within the newly created JavaScript file:

  1. lines 1-4 – Create a new JavaScript Object class
  2. lines 6-22 – Add methods to the Object prototype to call the PhoneGap.exec() function with a string identifying the corresponding method in your PhoneGapCommand class and pass any needed parameters. Because these methods have been added to the object prototype, they are considered instance methods. Meaning you need to create an instance of the object before you can call these methods.
  3. lines 24-33 – Add a class method to the object to “install” the object at runtime which will instantiate the object and make it accessible via window.plugins
  4. line 35 - Tell PhoneGap about your plugin so that it will be automatically “installed” when PhoneGap initializes itself
function ActivityIndicator()
{

};

ActivityIndicator.prototype.show = function(message)
{
    PhoneGap.exec('ActivityIndicatorCommand.show', message);

};

ActivityIndicator.prototype.updateMessage = function(message)
{
    PhoneGap.exec('ActivityIndicatorCommand.updateMessage', message);
    
};

ActivityIndicator.prototype.hide = function()
{
    PhoneGap.exec('ActivityIndicatorCommand.hide');


};

ActivityIndicator.install = function()
{
    if(!window.plugins)
    {
        window.plugins = {};	
    }

    window.plugins.activityIndicator = new ActivityIndicator();
    return window.plugins.activityIndicator;
};

PhoneGap.addConstructor(ActivityIndicator.install);

Next up, you need to implement your desired methods in your PhoneGapCommand class. In the ActivityIndicatorCommand class I have created methods to hide, show, and update a message on an activity indicator. The class and method names must match exactly the strings that represent the PhoneGap commands that I am calling in my ActivityIndicator.js file.

You will notice that each method defines an array of arguments as well as a dictionary of options. This is a PhoneGap pattern and allows you to pass in as many arguments and options as you like.

//  ActivityIndicatorCommand.m
//  HelloPhoneGap
//  Created by Hiedi Utley on 4/8/11.
//  Copyright 2011 Chariot Solutions, LLC. All rights reserved.
//
#import "ActivityIndicatorCommand.h"
#import "DSActivityView.h"
@implementation ActivityIndicatorCommand
- (void) show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    NSString * message = [arguments objectAtIndex:0];
    [DSBezelActivityView newActivityViewForView:[[self appViewController] view] withLabel:message];
}
- (void) hide:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    [DSBezelActivityView removeViewAnimated:YES];
}
- (void) updateMessage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
    NSString * message = [arguments objectAtIndex:0];
    [DSBezelActivityView updateMessage:message];
}
@end

Note: the DSActivityView class that I am using to display the activity indicator and message is from a previous post and is based on open source that I have modified to allow the updating of an existing message.
 

Putting it All Together

Once you have created the JavaScript and implemented the Objective-C classes, you then need to add your JavaScript to your HTML page and try it out.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <!-- Change this if you want to allow scaling -->
        <meta name="viewport" content="width=default-width; user-scalable=no" />
        
        <meta http-equiv="Content-type" content="text/html; charset=utf-8">
            <link rel="stylesheet" href="HelloPhoneGap.css" type="text/css"/>
            <title>HelloPhoneGap</title>
            <script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script>
            <script type="text/javascript" charset="utf-8" src="ActivityIndicator.js"></script>
            <script type="text/javascript" charset="utf-8">
                var ai;
                function onBodyLoad()
                {
                    document.addEventListener("deviceready",onDeviceReady,false);
                }
                /* When this function is called, PhoneGap has been initialized and is ready to roll */
                function onDeviceReady()
                {
                    ai = window.plugins.activityIndicator;
                }
                function showActivityIndicator(message)
                {    
                    ai.show(message);
                    window.setTimeout('ai.hide()', 2000);                    
                    
                }
                function showActivityIndicatorUpdateMessage(message)
                {
                    ai.show(message);
                    window.setTimeout('updateMessage()', 2000); 
                    window.setTimeout('ai.hide()', 5000);
                }
                function updateMessage()
                {
                    ai.updateMessage('I am a new message!!!');
                }
                </script>
            </head>
    <body onload="onBodyLoad()">
             <button onclick="showActivityIndicator('I am an activity indicator!')">Show Activity Indicator</button> <br>
             <button onclick="showActivityIndicatorUpdateMessage('I am an activity indicator!')">Show Activity Indicator with Message Update</button>
    </body>
</html>

Where to get the Code?

If you are using PhoneGap 0.9.6

You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap

If you are using PhoneGap 1.0

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

Well that’s all for now! Go write your first plugin!

PhoneGap Tutorial Series – #5 Third-Party Plugins (NativeControls)

March 30, 2011 16 comments

 

Extending the PhoneGap API – Third-Party Plugins (NativeControls)

 
Continuing on down the path of using third party plugins, today we will look at a little more complex example and use the NativeControls plugin to display a UIActionSheet to allow the user to select whether they want to take a photo using the camera or pick one from the photo library by utilizing the PhoneGap Camera API.

If you haven’t read my previous post on Third-Party Plugins (ChildBrowser) you may want to peruse that to understand the structure of a plugin and how to go about installing one before continuing on.
 

Native Controls Plugin

The NativeControls plugin provides access to several native controls that are commonly used in iOS iPhone development. The plugin for the controls provides access to varying levels of functionality:

  1. UIActionSheet – provides a slide up control with one to many buttons, the plugin allows the creation of the actionSheet, adding of buttons, and a delegate to respond to a user selection
  2. UIStatusBar – the plugin provides the ability to hide the standard status bar
  3. UIToolBar – provides a toolBar control with one “refresh” button, the plugin support for controlling the toolBar is currently somewhat limited
  4. UITabBar – provides a tabBar control with with one to five buttons, the plugin allows the creation of the tabBar, adding buttons, button actions, positioning, and show/hide capabilities


 

How to Use the Native Controls Plugin?

Just like with the ChildBrowser plugin, there is a JavaScript file that must be included on the HTML page. After inspecting that Javascript, you will see that near the end of the file there is a reference to the PhoneGap.addConstructor so this particular plugin “installs” itself when you include the NativeControls.js on the page.

PhoneGap.addConstructor(function() 
{
	if(!window.plugins)
	{
		window.plugins = {};
	}
    window.plugins.nativeControls = new NativeControls();
});

The following is an excerpt from the nativeControls.html in the HelloPhoneGap project. The code demonstrates how to use the nativeControls plugin to create and display an actionSheet and how to react to a user selection in order to exercise the PhoneGap Camera API.

<script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script>
<script type="text/javascript" charset="utf-8" src="NativeControls.js"></script>
<script type="text/javascript" charset="utf-8">
                var nativeControls;                    
                function onBodyLoad()
                {
                    document.addEventListener("deviceready",onDeviceReady,false);
                }
                /* When this function is called, PhoneGap is ready to roll */
                function onDeviceReady()
                {
                    phoneGapReady.innerHTML = "PhoneGap is Ready";
                    nativeControls = window.plugins.nativeControls;
                }
                function showCameraOptions()
                {
                    var buttons = ["Take Photo", "Choose From Library", "Cancel"];
                    var delegate = nativeControls.createActionSheet(buttons, null, 2, null);
                    delegate.onActionSheetDismissed = function(index)
                    {
                        if (index == 0)
                        {
                            navigator.camera.getPicture(onPhotoURISuccess, onFail, {quality:5,destinationType:1,sourceType:1,allowEdit:false});
                        }
                        else if (index == 1)
                        {
                            navigator.camera.getPicture(onPhotoURISuccess, onFail, {quality:5,destinationType:1,sourceType:0,allowEdit:false});
                        }            
                    }
                };
                function onPhotoURISuccess(imageURI) {

                    var myImage = document.getElementById('myImage');
                    myImage.style.display = 'block';
                    myImage.src = imageURI;
                }
               function onFail(mesage) {
                    alert('Failed because: ' + message);
                }  
                </script>
            </head>
    <body onload="onBodyLoad()">
           <button onclick="showCameraOptions();">Display Photo</button> <br>
           <img height=200 width=200 id="myImage" /> 
    </body>

 
There are a number of things to take note of here:

  1. line 2 – NativeControls.js is included after the phonegap.js file
  2. line 13 – shortened reference to window.plugins.nativeControls is created
  3. lines 15-19 – function showCameraOptions() creates the action sheet and assigns a delegate function for when the user selects an option
  4. lines 19-30 – defined anonymous function to get a photo depending on whether user opts for the camera or the library
  5. lines 31-39 – onSuccess and onFailure functions defined for the camera API

 

Putting It All Together

So there is actually quite a bit going on here and I think it may be helpful to see a diagram that explains how all this actually works. If you haven’t read my post about PhoneGap Project Structure and Internals you may find it helpful for understanding the following interactions in more detail.

The diagram has several areas that I’d like to call your attention to:

  1. nativeControls.showTabBar() - executing a call to a third-party plugin
  2. PhoneGap.exec(…) - executing PhoneGap API to run a command
  3. PhoneGap.runCommand(…) – ultimately changing the document.location on the UIWebView
  4. PhoneGapDelegate – document.location change causes the UIWebViewDelegate (aka the PhoneGapDelegate) to intercept the request
  5. PhoneGapDelegate – request is inspected and forwards to the appropriate PhoneGapCommand
  6. PhoneGapCommand – executes requested operation and then notifies the UIWebView by executing the JavaScript onSuccess or onFailure callbacks

The gist is that every time you execute a PhoneGap API function or a function from a third-party plugin it will ultimately end up changing the document.location of the webView which will be intercepted by the webViewDelegate and then forwarded on to the appropriate PhoneGapCommand class.

Once the command has completed it will typically turn around and execute [webView stringByEvaluatingJavaScriptFromString:jsCallBack]; to call either the provided onSuccess or onFailure js callback functions.

In the example with the ActionSheet and the Camera, we are actually running through the loop between the JavaScript runtime and the Objective-C runtime twice, once for showing the ActionSheet and getting the user response, and once for showing either the camera or the photo library to get a photo.
 

Where to get the Code?

The plugins used are from Jesse MacFayden (aka @purplecabbage on twitter). The original source code for the plugins can be downloaded from github here: https://github.com/purplecabbage/phonegap-plugins.git

If you are using PhoneGap 0.9.6

You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap

If you are using PhoneGap 1.0

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0

That’s all for now – stay tuned for the next post on creating your own PhoneGap plugin.

PhoneGap Tutorial Series – #4 Third-Party Plugins (ChildBrowser)

March 30, 2011 85 comments

 

Extending the PhoneGap API – Third-Party Plugins (ChildBrowser)

 
So the last post was all about editing PhoneGap classes to add a little something extra, today it’s about using a third-party plugin that you may have downloaded from somewhere or gotten from someone.

The plugins that we will be using today are from Jesse MacFayden (aka @purplecabbage on twitter). The original source code for the plugins can be downloaded from github here: https://github.com/purplecabbage/phonegap-plugins.git

If you are using PhoneGap 0.9.6

You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap

If you are using PhoneGap 1.0

You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0
 

What is a PhoneGap Plugin?

Simply enough, a PhoneGap plugin is an extension to PhoneGap that allows access to some piece of native functionality on the phone that PhoneGap doesn’t already provide.

A PhoneGap plugin consists of at least two pieces:

  1. JavaScript file that defines the functions for accessing the native hooks
  2. Implementation files written in the native language to interact with native phone features

So for iOS a PhoneGap plugin is a package that consists of at least one JavaScript file and at least a pair of .m and .h Objective-C files that extend the PhoneGapCommand class.

It is possible (depending on the complexity of the plugin) that there will be other files as well such as UIViewControllers, UI.xib files, images, etc.
 

Installing a Third-Party Plugin

Once you have a copy of the plugin that you want to use, you need to make it accessible to your project. This can be done in a variety ways and the plugin could be located in a number of places. To keep it simple and not venture into a long rant on packaging, we will place our newly acquired plugin in the ${PROJECT_DIR}/Plugins directory.

    General Steps to Follow:

  1. Download your plugin to your machine somewhere – take note of the location
  2. Open your XCode project (which was created from the PhoneGap template)
  3. Locate the Plugins directory within your project (not the PhonGapLib project)
  4. Right-click and add the files from your downloaded plugin directory. Be sure to check the “copy” and “create group references” checkboxes
  5. Build the project — you may need to add dependent libraries depending on the plugin

In my HelloPhoneGap example I have copied in two plugins from @purplecabbage:

  1. ChildBrowser – plugin to open resources in a child browser of the application rather than launching Safari.
  2. NativeControls – plugin to use native controls like a TabBar and ActionSheet (which I will demonstrate in an upcoming post).

As you can see from the above screenshot, the ChildBrowser plugin has images, a viewController, a .xib file, as well as JavaScript and the PhoneGapCommand implementation. The NativeControls plugin is a bit simpler with just the three typical files.

In order to use these plugins from JavaScript the script files need to be located in the ‘www’ directory and you need to include the proper script file on your HTML page. For peace of mind, I added a build step to copy the .js files from the ${PROJECT_DIR}/plugins directory to the ${PROJECT_DIR}/www.

I execute the following script during the build to copy the files:

#!/bin/sh
#  PluginCopy.sh
#  HelloPhoneGap
#  Created by Hiedi Utley on 3/30/11.
#  Copyright 2011 Chariot Solutions, LLC. All rights reserved.
cp -rf ${PROJECT_DIR}/Plugins/*/*.js ${PROJECT_DIR}/www

Note: You will need to chmod the PluginCopy.sh script file once you download it from github in order to build the HelloPhoneGap project.
 

How To Use the Plugin

Using a plugin is relatively simple, after you have put all the files in place in the project, you just need to include the script file for the plugin on your page. Depending on the plugin – you may need to explicitly “install” the plugin – which just means instantiate an instance of the plugin object and make it available for use.

Typically plugins are added to the window.plugins object and are accessible like window.plugins.somePlugin but sometimes that can get unwieldy and you may want to shorten the reference to something like var somePlugin = window.plugins.somePlugin; for use on your page. It’s up to you…

As for figuring out if you need to explicitly install a plugin, all you need to do is inspect the JavaScript (and/or the README) for the plugin and look for a call to the PhoneGap.addConstructor if that’s there, then you should be able to access the plugin as soon as onDeviceReady() is called. If not – you may need to do a SomePlugin.install() within onDeviceReady before using the plugin.
 

Sample Code

The following excerpt is from the childBrowser.html page in the HelloPhoneGap project – and there are several things to note:

  1. line 9 - PhoneGap.js is included before any other plugin files
  2. line 10 – ChildBrowser.js file is included – this file should be located in the ${PROJECT_DIR}/ www directory
  3. lines 13-22 – onDeviceReady() method signifies that PhoneGap has been initialized
  4. line 21 – ChildBrowser.install() exlicitly installs the ChildBrowser plugin. Note some plugins will do this automatically. Check…
  5. lines 23-34 - openChildBrowser(url) calls the childBrowser.showWebPage(url) function to launch a new UIWebView with the requested URL
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <!-- Change this if you want to allow scaling -->
        <meta name="viewport" content="width=default-width; user-scalable=no" />
        <meta http-equiv="Content-type" content="text/html; charset=utf-8">
            <link rel="stylesheet" href="HelloPhoneGap.css" type="text/css"/>
            <title>HelloPhoneGap</title>         
            <script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script>
            <script type="text/javascript" charset="utf-8" src="ChildBrowser.js"></script>
            <script type="text/javascript" charset="utf-8">
                var childBrowser;
                function onBodyLoad()
                {
                    document.addEventListener("deviceready",onDeviceReady,false);
                }
                /* PhoneGap has been initialized and is ready to roll */
                function onDeviceReady()
                {
                    phoneGapReady.innerHTML = "PhoneGap is Ready";
                    childBrowser = ChildBrowser.install();
                }
                function openChildBrowser(url)
                {
                    try {
                        //both of these should work...
                        //window.plugins.childBrowser.showWebPage(url);
                        childBrowser.showWebPage(url);
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }
                </script>
            </head>
    <body onload="onBodyLoad()">
           <button onclick="openChildBrowser('http://www.google.com');">Open Google</button> 
    </body>
</html>

So the screenshots show the childBrowser.html page and by clicking on the button, the plugin is called which ultimately instantiates a instance of the ChildBrowserViewController which loads the ChildBrowserViewController.xib file to display a new UIWebView and load the requested URL.

As a side note, I also wanted to be able to open local file resources in my ChildBrowser (which it wasn’t able to do before) so I did edit the plugin files to make this happen. If you are interested in the changes – please peruse the ChildBrowserCommand.m file lines 36-44.




So that’s all for now — next post will concentrate on using the NativeControls (ActionSheet) plugin to interact with the built-in PhoneGap Camera API.

PhoneGap Tutorial Series – #3 Extending the PhoneGap API

March 28, 2011 8 comments

 

Extending the PhoneGap API

 
If you’ve had a chance to play with PhoneGap a bit, chances are you have wanted it to do something that it doesn’t already do. Alas, don’t worry! You don’t have to put in a ticket and hope that the PhoneGap developers agree with you and implement it in some future release, if you can write a little Objective-C then you can do it yourself and use it in your own iOS project.

The topic at hand is all about extending the PhoneGap API whether it’s by editing existing PhoneGap classes, downloading a third-party plugin from somewhere, or by writing your own plugin from scratch. For this post I will concentrate on editing the PhoneGap classes, in later posts I’ll give step-by-step instructions on using a third-party plugin and how to create your own plugin.

If you haven’t already had a chance to read my earlier posts on PhoneGap internals and using the PhoneGap API – you may want to peruse them before reading on.
 

Adding Functionality to the PhoneGap Classes

 
Off the top of my head, one of the things that I want PhoneGap to do is to take a picture (which it already can) and save it in the photo library (which it doesn’t do). Who knows why this isn’t already in the API but it’s something that I would like. So I could just write all my own Objective-C to take the picture and save but I don’t really want to redo something that is there, I just want to add a little something more to it.

How to Save a Photo to the Library

So the first thing that I need to find out is how to actually save an image to the photo library. After googling around a bit I found that the following UIKit reference from Apple indicates that this method: UIImageWriteToSavedPhotosAlbum should accomplish what we want.

//Adds the specified image to the user’s Camera Roll album.
void UIImageWriteToSavedPhotosAlbum (
   UIImage  *image,
   id       completionTarget, //optional
   SEL      completionSelector, //optional
   void     *contextInfo //optional
);


How Does the Camera API Work?

Next we need to take a look under the hood at what the PhoneGap Camera API already does when we tell it to take a picture and find an appropriate place to inject our own code. Since PhoneGap is open source we can do this and make all the changes that we want on our own behalf – but keep in mind that if you upgrade you will have to make your changes again.

This is one of many reasons to write a new plugin instead of editing PhoneGap directly – the instructions for which will be in a future post.

In the Camera.h file from the PhoneGapLib project (version 0.9.4) we see that they define a CameraPicker interface that extends the UIImagePickerController and that the Camera implements the UIImagePickerControllerDelegate.

The delegate defines the imagePickerController:didFinishPickingMediaWithInfo method that is called when the UIImagePickerController has selected an image.

@interface CameraPicker : UIImagePickerController
//removed ....
@end
@interface Camera : PhoneGapCommand<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
	CameraPicker* pickerController;
}

For those that are interested, the Camera Programming for iOS Guide from Apple explains in detail how to use the UIImagePicker API to interact with the camera and the photo library.

The following excerpt is from the Camera.m file from the PhoneGapLib project (version 0.9.4):

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
	CameraPicker* cameraPicker = (CameraPicker*)picker;
	CGFloat quality = (double)cameraPicker.quality / 100.0; 
	[picker dismissModalViewControllerAnimated:YES];
	NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
	if ([mediaType isEqualToString:(NSString*)kUTTypeImage])
	{
		if (cameraPicker.successCallback) {
			
			NSString* jsString = NULL;
							// get the image
				UIImage* image = nil;
				if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]){
					image = [info objectForKey:UIImagePickerControllerEditedImage];
				}else {
					image = [info objectForKey:UIImagePickerControllerOriginalImage];
				}

				NSData* data = UIImageJPEGRepresentation(image, quality);
				if (cameraPicker.returnType == DestinationTypeFileUri){
					// write to temp directory and reutrn URI
					// removed for brevity...
				}else{
					jsString = [NSString stringWithFormat:@"%@(\"%@\");", cameraPicker.successCallback, [data base64EncodedString]];
				}
			[webView stringByEvaluatingJavaScriptFromString:jsString];
		}
	}
}

The imagePickerController:didFinishPickingMediaWithInfo delegate method is doing a number of things:


  • line 6 – making sure that an image was selected
  • line 9 – making sure that a successCallback was defined
  • lines 13-18 – getting a reference to the selected image
  • lines 20-26 – writing the image to disk or defining an encoded string
  • line 27 – executing the JavaScript successCallback method

Where to Add Your Code?

The most appropriate place for us to inject our code to save the image is at line 19 just after we have gotten a reference to the image. The following code snippet checks to make sure that the image source was from the camera (not the library) and then saves the image into the photo library using the UIKit UIImageWriteToSavedPhotosAlbum method.

                //save the photo to the album
                if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera)
                {
                    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
                }

Since taking a picture with the PhoneGap API requires that you be running on a device, you must build and deploy to your iPhone in order to test that our code works.

    The expected outcome is:
  1. Without the changes, the Camera API should allow you to take a picture BUT will not save it to the photo library.
  2. With the changes, the Camera API should allow you to take a picture AND will save it to the photo library. You should be able to open the camera roll and see your new pic.

That’s all for now — stay tuned for more upcoming posts on PhoneGap…

Follow

Get every new post delivered to your Inbox.

Join 33 other followers