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, read Getting 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 in custom 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 articleGetting 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:
- Right-click the Plugins directory inside of your PhoneGap project, and select New File (see Figure 1).
- In the New File wizard, select the Objective-C class template and click Next (see Figure 2).
- Type HelloPlugin for the new class name and make the class a subclass of CDVPlugin (see Figure 3).
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.
- Click Next.
- 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).
- 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.
- 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 appropriateCDVPluginResult
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.
- 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>
- Also after the
onDeviceReady()
function, add the JavaScript for invoking the native plugin and handling the plugin results. Add JavaScript functions namedcallNativePlugin
,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 ); }
- The
callNativePlugin
function will simply call the JavaScript interface of the native plugin class. When it invokes thecallNativeFunction
method, it passes the callback functions for success and error status received from the native code layer. ThenativePluginResultHandler
function will be invoked if there is a success callback from the native layer, and thenativePluginErrorHandler
function will be invoked upon an error callback from the native layer.
- 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 the nativePluginResultHandler
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 the nativePluginErrorHandler
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.
- In the project navigator, open the Supporting Files folder, and click the file named Cordova.plist.
- On the right, scroll down to the Plugins entry and click to expand it.
- Add an entry with the key
com.tricedesigns.HelloPlugin
and the value"HelloPlugin"
(see Figure 5), and replacecom.tricedesigns
with your company identifier. This is the string reference that you used to identify the native class in the third parameter when callingCordova.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.
- Save your changes.
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.
When the native code is invoked, you will also be able to see output in the Xcode debug console window, which reflects the output from the native plugin's NSLog
call (see Figure 7).
Where to go from here
You now know how to start creating native plugins for PhoneGap applications on iOS devices. You can use this technique to access iOS frameworks that are not exposed by the core PhoneGap SDK, including (but not limited to) the Game Center, Core Audio, and Bonjour frameworks, or any other native code. Also, don't forget to check out the many existing PhoneGap native plugins on GitHub that have been developed by the open source community.