Advertisements

Archive

Posts Tagged ‘ios’

HelloPhoneGap1.0 Project updated for PhoneGap 1.1.0

October 28, 2011 33 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!

Advertisements

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 …

Xcode4 – Is your old project hanging in the simulator?

March 10, 2011 4 comments

 

So you took the plunge and upgraded to Xcode4…

 
Do your old projects build but won’t run on the simulator or even on a device? Well we spent a couple hours today trying to figure out why and after uninstalling/reinstalling, recreating projects, comparing settings, and every combination of simulator and SDK we finally stumbled on a solution.

So it seems I had some corrupt settings or project data that was preventing the app from running. By deleting all the xcuserdata associated with project it magically started to work again.

If you are having the same problem — deleting all the Xcode4 project data with your userid may get you up and running again…

    How to go about deleting the user-specific project data
  1. In terminal – navigate to your project directory
  2. cd into the ${PROJECT_NAME}.xcodeproj directory
  3. run this command: find . -name ‘*yourUserName*’
  4. rm -rf any files or dirs that come up
  5. Reopen Xcode4 – build and *hopefully* run your project

Hope it helps!