Archive

Archive for the ‘Objective-C’ Category

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!

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…

Xcode3 – Debugging iOS Unit Tests

March 8, 2011 4 comments

 

So you have some unit tests (yeah!) and they are failing …. now what?

 
Over the course of my career these past few years, I have become a developer that writes more test code than production code in an effort to never have to spend the wee hours of the morning debugging a horrible production issue. As a java developer turned mobile developer, there are several things that I miss about coding in Java but the number one thing is unit tests.

Don’t get me wrong – I know what you are thinking – probably something along the lines of: “Do a little research dummy! Xcode has a nice Unit Test Bundle target that we can use to run unit tests! All you have to do is write them!” Right…..

Or maybe after a little more googling around you may even point me to this excellent Apple documentation that explains the process of setting up and running my newly created unit tests but there is a huge section missing from this guide…

iOS Development Guide: Unit Testing Applications

So I have followed all these steps and everything is running fine and dandy until …I need to debug my tests. Where is the guide for this? What’s the point of having tests if I can’t debug them???

Having spent countless hours reading blog posts and attempting steps I have finally gotten it working with Xcode 3.2 AND iOS 4.2 as of today! Yes today! Not two years ago and not on some old version of Xcode and some weird version of the iOS SDK so to save you from having to experience this same misery keep on reading.
 

Have no fear! Instructions are finally here!

 
This guide assumes that you have the following knowledge, skills, and tools:

  1. Apple computer that has the Developer Tools installed and up to date.
  2. Working knowledge of Xcode 3.2, Xcode projects, Xcode build targets
  3. Working knowledge of Objective-C and iOS development

With the above, you should be able to follow these steps fairly easily so I won’t go into details on the typical tasks. If you need more information on how to use Xcode or setting up targets please see the following guide: Understanding Xcode Projects

Here we go…

    Create a new Xcode project

  1. Open Xcode 3 (instructions coming later for Xcode4)
  2. Create a new Xcode project (iOS window-based application template) — mine is called “MyNewApp”
  3. Build and Run the project for the simulator – if its “just working” – and it should – you should see a blank white screen in the simulator.

    Create a new target and run some tests

  1. Select “Targets” and add a new target (Unit Test Bundle template) — mine is called “MyNewAppTests”
  2. Expand the build phases of the new target and take note of the last step – “Run Script” – this is the step that runs your tests as part of the build.
  3. Add a new Objective-C Test class to the project, make sure you add it to the “MyNewAppTests” target.
  4. Within the new “MyNewAppTests.m” file add the following code:
    - (void) testFail {
    STFail(@"Must fail to succeed.");
    }
    
  5. Select the “MyNewAppTests” as the active target and run the build – it should fail

These steps are a shortened version of this guide: iOS Development Guide: Unit Testing Applications. If you need more detail to get your initial units tests building and running – please see the full documentation.
 

It’s failing … now its time to debug!

 
To debug your tests you need to set up an alternate method to run the tests – the basic “Run Script” in the typical test bundle target actually runs a “RunUnitTests.sh” which ultimately launches an instance of “otest” to run the tests — but its not debuggable and runs standalone.

In order to create your own executable to run “otest” and debug it – you will need to run the following command to find the right otest executable for your current iOS SDK.

Run this in the Terminal: find /Developer -name otest

When I run this I see a number of options — select the one from the latest iPhoneSimulator#.#.sdk — and take note of the path.

[hiedi:/Developer/Tools] % find /Developer -name otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.2.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.0.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/Developer/usr/bin/otest
/Developer/Source/OCUnit/SourceCode/otest

 

    Create another new target and an executable

  1. Select “Targets” and add a new target (Unit Test Bundle template) — mine is called “MyNewAppTestsDebug”
  2. Expand the build phases of the new target — delete the last build phase — “Run Script”
  3. Right click and “Get Info” on the “MyNewAppTests.m” — add “MyNewAppTestsDebug” to the list of included targets.
  4. Select “Executables” and add a new custom executable with any name — mine is “Debuggable Otest” and fill in the path that you found above.
  5. On the arguments tab — add an argument that specifies the test bundle to use — mine is “MyNewAppTestsDebug.octest”.

On the arguments tab — also add the following environment variables:

Variable Value
DYLD_FRAMEWORK_PATH ${SDKROOT}/Developer/Library/Frameworks
DYLD_LIBRARY_PATH ${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}
DYLD_NEW_LOCAL_SHARED_REGIONS YES
DYLD_NO_FIX_PREBINDING YES
DYLD_ROOT_PATH $SDKROOT
IPHONE_SIMULATOR_ROOT $SDKROOT

    Run the new target and executable

  1. Set the new target and the new executable as active and then Build/Run
  2. Open the console – you should see messages relating to the running of your tests and their failure.
  3. Now set a breakpoint in the “MyNewAppTests,m” file then Build/Debug
  4. In the console you should see messages about resolving your breakpoint and then it should stop in the debugger.

Note: These steps have been adapted from an original blog post by the author of Grokking Cocoa and can be found here: How to Debug iPhone Unit Tests

Activity Indicators

Today I spent a fair amount of time trying to write my own UIView that I could use to display a UIActivityIndicator and a message to a user when I am loading some things in the background. At first this didn’t seem all that hard and I had a working prototype in a matter of minutes using a standard nib and controller and I wired everything together. Then came the wrinkle — I need to show this new view over top of an existing view and have it paint on the main thread while I am downloading some content. Problem is that I tied up the main thread trying to do my download.

Once I finally figured out how to get my view to paint and then do the download I then found that I couldn’t make my view completely transparent — it was always black! Arrgh. So rather than spend anymore time trying to display a separate view I decided to change tactics and just add subviews onto my current view controller and I went searching around and found a GREAT project that is easy to use and saved me an afternoon of coding myself.

So if you are struggling to show a UIActivityIndicator in an elegant fashion then look to this site for some help: DS Activity View Project