Search Results

Keyword: ‘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 14 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 82 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…

PhoneGap Tutorial Series – #2 Using the PhoneGap API

March 17, 2011 Leave a comment

 

Using the PhoneGap API

 
Welcome to the second installment of my PhoneGap Tutorial Series! This post is a continuation of PhoneGap Project Structure and Internals so if you haven’t already, you may want to peruse that post before continuing on.

We already know that PhoneGap is an open source framework for writing applications using typical web technologies like HTML, CSS, and JavaScript and that the PhoneGap architecture supplies JavaScript wrappers to access native phone features. What you may NOT know is how extensive those features really are or how to actually use them.

This article is all about getting familiar with the current features of the PhoneGap API and how to use them with the iPhone iOS.
 

So What Can PhoneGap Do For Me???

 
Well, turns out it can do a lot – right out of the box with “no coding” required (hahahahahaha! yeah right). No really, PhoneGap has an extensive library that already provides access to many of the iPhone’s basic features.

The following table outlines the PhoneGap API’s current features:

API Details
Accelerometer Tap into the device’s motion sensor.
Camera Capture a photo using the device’s camera.
Compass Obtain the direction that the device is pointing.
Contacts Work with the devices contact database.
Device Gather device specific information.
Events Hook into native events through JavaScript.
File Hook into native file system through JavaScript.
Geolocation Make your application location aware.
Media Record and play back audio files.
Network Quickly check the network state.
Notification Visual, audible, and tactile device notifications.
Storage Hook into the devices native storage options.

 

So How Do I Use These Great Features?

 
It’s actually pretty simple. If you have read the previous post on PhoneGap Project Structure and Internals then you already have a PhoneGap project started – if not – you can get the starter project here.

The PhoneGap documentation has a lot of examples to help you figure out how to code against the API. I have put together a project that exercises these examples and provides a working iPhone application for you to tinker with to see what PhoneGap can really do for you right out of the box.

In later posts I will extend the PhoneGap functionality, add additional third party plugins, and write my own plugin. All of the source code for the project is located on my github repository – https://github.com/hutley/HelloPhoneGap.

Note: I am updating the sample for each API as I get to it so if it says “Under Construction” just check back later for that sample.


Basic Pattern

Each PhoneGap API typically follows a pretty basic pattern where you provide onSuccess and onFailure JavaScript callback functions to each API call which means that when you call a PhoneGap method you will explicitly provide pointers to the JavaScript methods that are to be invoked when the API either succeeds or fails.

Some of the APIs perform this as an asynchronous operation so you cannot count on a specific order of events. If you need a synchronous pipeline – then you need to chain the events through the callback methods. Check the API…



Accelerometer Example

The following code snippet is an HTML page that includes PhoneGap and exercises the Accelerometer API.

There are several things to notice here:

  • Line 5 – PhoneGap.js must be included on the page (at least before third party plugins or custom plugins).
  • Line 9 – onBodyLoad() function adds a event listener for “deviceready”, when this event is triggered, then PhoneGap is initialized.
  • Line 18 – navigator.accelerometer. getCurrentAcceleration(onAccelerationSuccess, onError) – call to PhoneGap API passing OUR success/failure functions.
  • Line 34 - button to execute the getCurrentAcceleration() function


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
         <meta http-equiv="Content-type" content="text/html; charset=utf-8">
            <script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script>
            <script type="text/javascript" charset="utf-8">
                function onBodyLoad()
                {
                    document.addEventListener("deviceready",onDeviceReady,false);
                }
                
                /* When this function is called, PhoneGap has been initialized */
                function onDeviceReady()
                {
                }
                
                function getCurrentAcceleration() {
                    navigator.accelerometer.getCurrentAcceleration(onAccelerationSuccess, onError);
                }
                
                // onSuccess: Get a snapshot of the current acceleration
                function onAccelerationSuccess(acceleration) {
                    alert( 'Acceleration X: ' + acceleration.x + '<BR>' + 'Acceleration Y: ' + acceleration.y + '<BR>' 
                             + 'Acceleration Z: ' + acceleration.z + '<BR>');
                }

                // onError: Failed to get the acceleration
                function onError() {
                    alert ("onError");
                }
                </script>
            </head>
    <body onload="onBodyLoad()">
                        <button  onclick="getCurrentAcceleration();">getCurrentAcceleration()</button>  
    </body>
</html>


Little Gotchas!

So in the course of putting this together, I noticed that lots of the samples that I was trying to get working just seemed to do nothing. Turns out that there are a lot of little things that just don’t work in the simulator and PhoneGap chooses to silently perform a no-op. So instead of calling the onFailure methods, PhoneGap just returns from the API call without doing anything.

Awesome? No. Now maybe it should be obvious to me that I can’t take a pic from the simulator – BUT my MacBook has a nice little camera so it wasn’t…. Anyway – after stepping through the Objective-C code – I stumbled on this:

- (void) getPicture:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	NSString* successCallback = nil, *errorCallback = nil;
	
	if (argc > 0) successCallback = [arguments objectAtIndex:0];
	if (argc > 1) errorCallback = [arguments objectAtIndex:1];
	
	if (argc < 1) {
		NSLog(@"Camera.getPicture: Missing 1st parameter.");
		return;
	}
	
	NSString* sourceTypeString = [options valueForKey:@"sourceType"];
	UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; // default
	if (sourceTypeString != nil) {
		sourceType = (UIImagePickerControllerSourceType)[sourceTypeString intValue];
	}

	bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType];
	if (!hasCamera) {
		NSLog(@"Camera.getPicture: source type %d not available.", sourceType);
		return;
	}

Notice that in both line 11 and line 23 there are return statements that basically perform a silent failure – yes there are log entries but since I am providing an onFailure method I would think it would get called. That is not the case – so just beware of the quirks when using some of these features.

Well that’s all for now – stay posted for the next installment….

PhoneGap Tutorial Series – #1 Project Structure and Internals

March 14, 2011 9 comments

 

What is PhoneGap?

 
PhoneGap is an open source framework for writing applications using typical web technologies like HTML, CSS, and javascript. The PhoneGap architecture supplies javascript wrappers that allow a developer to access native phone features (like contacts, GPS, and the camera) by writing their app against the PhoneGap javascript API.

The basic gist is that the typical web developer will be able to write a standalone HTML/CSS/javascript application that runs in a WebKit browser with access to native functions just by calling the PhoneGap javascript. PhoneGap aides the developer by abstracting the complexities of native development by allowing the developer to write the app in well-known technology without having to learn the intricacies of each mobile platform and thereby hiding the complexity of writing the same application multiple times in various native languages (like Objective-C for the iPhone or Java for Android).

Since I have been focusing on iOS development, this series of blog posts will focus on using PhoneGap to create an iPhone application. The series will walk you through the PhoneGap architecture, using the PhoneGap API, installing a third party plugin, and creating your own custom PhoneGap plugin.

The following posts assume a fair degree of familiarity with programming concepts and the Apple developer tools. Please review the following links for information on any topics where you may need additional education or explanation.

Assumed knowledge:

  1. General web development (HTML/CSS/JS)
  2. General programming knowledge (flow control, data structures etc)
  3. General Mac skills (Terminal, Finder etc)

Pre-requisites:

  1. Mac computer with Developer Tools (Xcode) installed and up to date. See this link for instructions: http://developer.apple.com/xcode/
  2. If deploying to an iOS device, an Apple Developer membership. See this link for more information: http://developer.apple.com/programs/ios/
  3. PhoneGap installation and configuration. See this link for instructions: http://www.phonegap.com/start#ios

 

How Does PhoneGap Work on the iPhone?

 
To get started I’d like to explain a little bit about how PhoneGap iOS project is setup and how PhoneGap actually works on the iPhone so that you can better understand the course of events that take place when you execute a PhoneGap function.

The first thing you will need to do is set up a PhoneGap project so please follow the Getting Started guide for the iOS PhoneGap project to install PhoneGap and create a PhoneGap project from the PhoneGap template.

Note: You will need to complete these steps using Xcode3 as Xcode4 does not yet have supported phone gap templates – if you do not have access to Xcode3 click here for a starter project.

 

PhoneGap Project Structure:

Once you have created a project based off of the Phone Gap Template then we are ready to get started with a tour of the Xcode project:

In the screenshot you will notice that there are numbered areas that I would like to call your attention to:

1. ‘www’ folder — this is the folder where all of the static resources for the web view should be stored. This is also the location where the phoneGap build steps will copy the phonegap.js files and where you will place all of your own HTML/CSS/JS files.

2. index.html / phonegap.js script — the index.html is the start page of your application. In order to access PhoneGap functions you must include the phonegap.js script reference on the index.html and any other html page from where you will call PhoneGap functions.

3. PhoneGap classes — PhoneGap provides an extension to the typical iOS classes for the UIApplicationDelegate (which implements the UIWebViewDelegate methods) and the UIViewController. These classes perform most of the “magic” behind PhoneGap such as setting up the webView and wiring together the view controller and delegates.

4. PhoneGap commands — PhoneGap provides a number of built-in plugins to access native resources like the camera or the address book, this is where you can find the source code…

5. Project Plugins — This is where you would place your own custom plugins since PhoneGap can be easily extended and new features added by extending the PhoneGapCommand class and implementing the objective-c code for the native function you want to expose (see later in the tutorial for how to do this.)

 

So … How Does it all Work?

PhoneGap works by extending and wrapping common classes of the iOS SDK:

1. The PhoneGapDelegate class extends UIApplicationDelegate and implements the UIWebViewDelegate protocol and is responsible for setting up the application, instantiating the view controller, configuring the views, and performing the delegate functions for the webView.

2. The PhoneGapViewController extends UIViewController and is responsible for responding to view lifecycle methods.

3. The PhoneGapCommand is the base class for all the available PhoneGap API plugins (like the Camera, GPS, and Contacts) and is responsible for providing access to the appDelegate, appViewController, and the webView. This class can be extended to create your own custom plugins.

Putting it all together…

Basically, PhoneGap works by intercepting URL requests for the UIWebView that is loaded in the view. The PhoneGapAppDelegate implements the UIWebViewDelegate protocol to detect and intercept changes to the document.location of the UIWebView. Once intercepted, PhoneGap interrogates the request to determine what to do with it.

There are several url patterns that PhoneGap handles right out of the box:

1. gap:// command — this represents a request to execute a PhoneGap command, this string is generated by calling something like “PhoneGap.exec(‘SomePlugin.someMethod’, someArg1, someArg2);” in javascript which ultimately ends up being translated into a URL request of gap://SomePlugin.someMethod?arg1Name=someArg1&arg2Name=someArg2

This gap:// url is then set into the document.location of the WebView (by the PhoneGap javascript) and then intercepted by the PhoneGapAppDelegate (UIWebViewDelegate – webView:shouldStartLoadWithRequest method) and ultimately instantiates and executes the Objective-C PhoneGapCommand.

Once the Objective-C PhoneGapCommand has completed it may (or may not) notify the webView that it has completed. This is defined by the API but typically there is some sort of onSuccess and onFailure javascript method defined for each API call. The underlying document is notified of the results of the Objective-C operation by executing the onSuccess or OnFailure method — [webView stringByEvaluatingJavascriptFromString:@”onSuccess()”].

See this link for a more detailed explanation of this process – PhoneGap for the iPhone Exposed.

2. file://www/someurl.html — this represents a request to load a local file (possibly from the ‘www’ directory) into the webView

3. http://someweburl.html — this represents an http request to load a remote file into the webview

4. mailto: sms: tel: etc – there are several protocols build into the UIWebView to handle various functions like sending email or making a phone call which will cause the appropriate iPhone app to open and handle the request

5. Custom — you can override the PhoneGapAppDelegate webView:shouldStartLoadWithRequest method in order to implement your own custom URL handler.

Well that’s all for now … Stay posted for the next installment of the PhoneGap Tutorial Series – #2 Using the PhoneGap API …

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!

Follow

Get every new post delivered to your Inbox.

Join 34 other followers