Friday 19 October 2012

Extending PhoneGap with native plugins for iOS


This article examines native plugins for PhoneGap (also known as Apache Cordova) applications in Xcode, targeting iOS devices. If you are just starting out with PhoneGap or if you need to review the fundamentals of PhoneGap, readGetting started with PhoneGap in Xcode for iOS before continuing.
The terms Cordova and PhoneGap are used interchangeably within this article to refer to the same open source application platform that lets you create natively-installed mobile applications using HTML and JavaScript. The PhoneGap codebase moved to open source at Apache Software Foundation under the name Cordova. Adobe is still distributing it under the name PhoneGap. For more information, check out Brian Leroux's blog post "PhoneGap, Cordova, and what's in a name?" As Brian says in the post, "Currently, the only difference is in the name of the download package and will remain so for some time."
Not only does PhoneGap enable you to build user interfaces for natively installed mobile applications using web technologies, PhoneGap also provides a JavaScript-based API to interact with native device functionality. By default, PhoneGap provides access to the device camera, accelerometer, file system, GPS location, and media playback among other capabilities. However, PhoneGap does not expose every native API for use within your JavaScript applications. If you want PhoneGap to do more than its default feature set, you can use the PhoneGap native plugin model to extend the capabilities of the core PhoneGap API.
Native plugins in PhoneGap are not like plugins in desktop browsers; rather they provide a way for you to plug incustom code to add to what the PhoneGap application framework can do. PhoneGap native plugins enable you to create completely new, custom functionality in native code, and expose that to your PhoneGap applications via PhoneGap's native-to-JavaScript bridge. This means that you can expose any native library or framework for use within your JavaScript-based PhoneGap applications.

Understanding the PhoneGap native plugin structure

Before you start to write PhoneGap native plugins, it will help to understand how the PhoneGap application container exposes native operating system functionality to JavaScript-based applications.
All Cordova APIs consist of two related parts: a JavaScript-based interface that can be accessed within your applications, and the corresponding native class for performing operations in native code. Typically, the JavaScript classes and the native classes have APIs that mirrors each other, so that they are easy to follow. The JavaScript class invokes the native code using the Cordova.exec() function. When it invokes Cordova.exec , it can pass in a result handler function, an error handler function, and an array of parameters to be passed into native code, as well as a reference to the native class's name and native function name. Cordova will manage the JavaScript-to-native communication, and you can focus on building your application.
To learn more about PhoneGap native plugins, take a look at the core API's source code, available at the Cordova wiki. The entire PhoneGap framework is built upon the same paradigm you'll find there.

Building your first plugin

To start building your first PhoneGap native plugin, you'll need to create a new PhoneGap project following the steps outlined in the article Getting started with PhoneGap in Xcode for iOS. I named my project MyFirstPhoneGapNativePlugin.

The JavaScript class

Once you have set up your Hello Xcode project, you're ready to create the JavaScript interface for the native plugin. You will need to create a class with functions that will mirror the logic exposed by the native code. Under the www folder, create a JavaScript file named HelloPlugin.js that contains the simple JavaScript class shown below.
var HelloPlugin = { callNativeFunction: function (success, fail, resultType) { return Cordova.exec( success, fail, "com.tricedesigns.HelloPlugin", "nativeFunction", [resultType]); } };
The HelloPlugin class has a single function named callNativeFunction , which accepts a success callback function, an error callback function, and a resultType string parameter. The callNativeFunction function wraps the Cordova.exec function, which will invoke the actual native code. There is no additional JavaScript inside of this class, but you can add JavaScript code here if you need to.
When Cordova.exec is invoked, it expects five parameters:
  • a reference to a success callback function (a function that will be invoked upon a successful response from the native code layer)
  • an error callback function (a function that will be invoked upon an error response from the native layer)
  • a string reference to the native code class (I cover this in more detail below)
  • a string reference to the name of the function that should be invoked
  • an array of parameters to be passed into the native code
Keep in mind that code execution between the JavaScript and native code layers is not synchronous, so you'll need to use callback functions and asynchronous coding practices when developing PhoneGap native plugins.

The Native class

To create the native code layer, start by creating a new native Objective-C class that extends the CDVPlugin class from the core Cordova API:
  1. Right-click the Plugins directory inside of your PhoneGap project, and select New File (see Figure 1).
Figure 1. Creating the new file.
Figure 1. Creating the new file.
  1. In the New File wizard, select the Objective-C class template and click Next (see Figure 2).
Figure 2. Selecting the Object-C class template.
Figure 2. Selecting the Object-C class template.
  1. Type HelloPlugin for the new class name and make the class a subclass of CDVPlugin (see Figure 3).
Figure 3. Naming the class.
Figure 3. Naming the class.
The CDVPlugin class is the parent class that all Cordova classes must extend. The CDVPlugin class encapsulates all logic necessary for native-JavaScript communication via the PhoneGAP API. The PhoneGap.exec function enables you to invoke functions on this new class. The CDVPlugin class has a core function named writeJavascript , which enables you to invoke JavaScript within the PhoneGap application's web view. All communication in the direction of native to web JavaScript must be done using the writeJavascript function.
  1. Click Next.
  2. When prompted specify a location for the new files (preferably the "Plugins" directory within your Xcode project), and then click Finish to proceed.
You should see the new header file (.h) and implementation file (.m) in your PhoneGap project (see Figure 4).
Figure 4. The new native class files.
Figure 4. The new native class files.
  1. Next, add a definition for the nativeFunction function in your .h file; for example:
#import <Cordova/CDV.h> @interface HelloPlugin : CDVPlugin - (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; @end
This definition has two parameters: an NSMutableArray of arguments received from the JavaScript layer and a dictionary (map) of options. For this example, you only need to worry about the array of arguments. The header file only contains method signatures; you do not include any application logic in the .h file.
  1. Once you've created your class signature, add your logic to the nativeFunction function instance in the .m file. Below you will see a sample Objective-C function used inside of this native plugin class.
#import "HelloPlugin.h" @implementation HelloPlugin - (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { //get the callback id NSString *callbackId = [arguments pop]; NSLog(@"Hello, this is a native function called from PhoneGap/Cordova!"); NSString *resultType = [arguments objectAtIndex:0]; CDVPluginResult *result; if ( [resultType isEqualToString:@"success"] ) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @"Success :)"]; [self writeJavascript:[result toSuccessCallbackString:callbackId]]; } else { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"Error :("]; [self writeJavascript:[result toErrorCallbackString:callbackId]]; } } @end
Inside of the nativeFunction method, you first need to get a reference to the NSString callbackId , which is used by the core PhoneGap API to map the response of this function back to the originating JavaScript that invoked this function.
Next this method writes a message out to the Xcode debug console using NSLog ; this just shows that it is executing native code.
After writing to the debug console, the function examines the resultType that was passed into the function, and creates the appropriate CDVPluginResult instance. The resultType value is a simple string. If resultType is "success" , the function will create a success result, and write the success callback to the JavaScript layer using the[self writeJavascript] function. Any other value for the resultType parameter will generate an error result, and the function will write the error callback to the JavaScript layer.
When you write back to the success or error callback functions in JavaScript, always use a CDVPluginResult instance. However, you can also use the writeJavascript function to pass any JavaScript string back to the JavaScript layer. This technique can even be used to push data from the native layer to the JavaScript layer in real time.

Invoking the plugin

Now that you have created a plugin, you can invoke it from within your PhoneGap application.
  1. First, you will need to add a reference to the new plugin's JavaScript interface class (HelloPlugin.js). Add a new<script> tag inside of your index.html file:
<script type="text/javascript" charset="utf-8" src="HelloPlugin.js"></script>
  1. Also after the onDeviceReady() function, add the JavaScript for invoking the native plugin and handling the plugin results. Add JavaScript functions named callNativePlugin , nativePluginResultHandler , andnativePluginErrorHandler as shown below:
function callNativePlugin( returnSuccess ) { HelloPlugin.callNativeFunction( nativePluginResultHandler, nativePluginErrorHandler, returnSuccess ); } function nativePluginResultHandler (result) { alert("SUCCESS: \r\n"+result ); } function nativePluginErrorHandler (error) { alert("ERROR: \r\n"+error ); }
  1. The callNativePlugin function will simply call the JavaScript interface of the native plugin class. When it invokes the callNativeFunction method, it passes the callback functions for success and error status received from the native code layer. The nativePluginResultHandler function will be invoked if there is a success callback from the native layer, and the nativePluginErrorHandler function will be invoked upon an error callback from the native layer.
  1. Next add two JavaScript buttons as shown in the code below to invoke the plugin.
<body onload="onBodyLoad()"> <h1>Hey, it's Cordova!</h1> <button onclick="callNativePlugin('success');">Click to invoke the Native Plugin with an SUCCESS!</button> <button onclick="callNativePlugin('error');">Click to invoke the Native Plugin with an ERROR!</button> </body>
When clicked, the first button will invoke the callNativeFunction method with the parameter "success". PhoneGap will then execute native code and invoke a success callback in the JavaScript layer (it will invoke thenativePluginResultHandler function).
When you click the second button, the callNativeFunction method with will be called with the parameter "error". PhoneGap will execute native code and invoke an error callback in the JavaScript layer (it will invoke thenativePluginErrorHandler function).

Mapping the native code class

At this point you have almost everything wired up and ready to go, but there is still one more step that you have to complete before you will be able to invoke native code from JavaScript.
You have to add a mapping so that Cordova can identify your native code class. Remember the string reference that you used to identify the native class when calling Cordova.exec? You need to map that string to the actual class instance in the Cordova.plist file. The Cordova.plist file contains all configuration information for the current Cordova project.
  1. In the project navigator, open the Supporting Files folder, and click the file named Cordova.plist.
  2. On the right, scroll down to the Plugins entry and click to expand it.
  3. Add an entry with the key com.tricedesigns.HelloPlugin and the value "HelloPlugin" (see Figure 5), and replace com.tricedesigns with your company identifier. This is the string reference that you used to identify the native class in the third parameter when calling Cordova.exec.
The key is the unique string reference that is used by PhoneGap.exec to map to the native code class. The value is the name of the actual native class that will be invoked.
  1. Save your changes.
Figure 5. Editing Cordova.plist.
Figure 5. Editing Cordova.plist.
Now you are ready to launch the application and test it out.
To launch the application click the Run button or choose Product > Run.
Once the application launches in the iOS Simulator (or on a connected device), you will see a simple interface with two buttons (see Figure 6). Click on either button to invoke the native plugin's native code, and an alert message will be displayed via JavaScript upon either a success or error callback.
Figure 6. The application running in the iOS simulator.
Figure 6. The application running in the iOS simulator.

No comments:

Post a Comment