Archive

Archive for the ‘iOS’ Category

HelloPhoneGap1.0 Project Updated for PhoneGap 1.3.0

January 10, 2012 1 comment

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

Where to get the Code? (PhoneGap 1.3.0 Required)

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

PS. MAKE SURE YOU INSTALL PHONEGAP!

Advertisements

Debugging iOS Apps Using Safari Web Inspector

November 22, 2011 11 comments

 

Enabling Remote Debugging using Safari Web Inspector for iOS

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

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

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

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

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

Kudos to @atnan for finding it!!

References

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

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

HelloPhoneGap1.0 Project updated for PhoneGap 1.1.0

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

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

August 17, 2011 23 comments

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

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

Where to get the Code? (PhoneGap 1.0 Required)

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

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.