Pushing Data to a PhoneGap Web View

UPDATE:

In PhoneGap 1.5, the naming conventions were changed to use the Apache Cordova naming conventions.   EX: PGPlugin is now CDVPlugin, phonegap.js is now cordova.js, etc…   If you are running into issues, please be sure to check the PhoneGap version and appropriate naming conventions.

ORIGINAL POST:

In my last post, which gave a crash course in PhoneGap Native Plugins, I discussed a scenario where you could use a native plugin and the writeJavascript function to “push” data into the UI/web view layer from the native code layer.   To elaborate on this scenario, I put together a sample application that demonstrates this concept in action.

I set up the sample application so that there is an extremely basic HTML user interface.   In that user interface, you can click a link which executes native code to start a timer (in the native code layer).   Once the timer is started, the user interface will be updated with a “started” message, and on every timer event, the user interface will be updated with the current time, as it is provided by the native code layer.   In this case, I am using a timer to simulate an external thread of execution, or stream of input from some other source (device or stream).   Take a look at the video below to see it in action:

Next, let’s examine how it works.

The HTML interface is very simple.   There is an <a> link, which calls the “doStart()” JavaScript function, an <h4> element to display status, and an <h2> element to display the content received from the native code layer. When the “doStart()” function is invoked, it calls the SamplePlugin JavaScript class’ “start()” function, passing a reference to the “successCallback()” function, which will get executed when a callback is received from the native layer.   The “updateContent()” function will just set the innerHTML value of the “output” element to the value passed as a parameter.

<!DOCTYPE html>
<html>
  <head>
  <title></title>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
	<meta charset="utf-8">

    <script type="text/javascript" charset="utf-8" src="phonegap-1.4.1.js"></script>
    <script type="text/javascript" charset="utf-8" src="SamplePlugin.js"></script>
    <script type="text/javascript">

    function doStart() {
        SamplePlugin.start( successCallback );
    }

    function successCallback() {
        document.getElementById( "status" ).innerHTML = "started";
    }

    function updateContent( data ) {

        document.getElementById( "output" ).innerHTML = data;
    }

    </script>
  </head>
  <body onload="onBodyLoad()">
	<h1>Hey, it's PhoneGap!</h1>
	<a href="javascript:doStart()">Start!</a>

    <h4 id="status"></h4>
    <h2 id="output"></h2>
  </body>
</html>

In the SamplePlugin.js file, which is the JavaScript component of the native plugin, you can see that there is a SamplePlugin class instance that has a “start()” function.   The “start()” function invokes the PhoneGap.exec() command to invoke native code.

var SamplePlugin = {
    start: function (success, fail) {
        return PhoneGap.exec(success, fail, "SamplePlugin", "start", []);
    }
};

In the native code, there is a basic class that extends the PGPlugin class.  When the user invokes the “start()” function in JavaScript, the native “start” function will be invoked.   In this function, output is appended to the console using NSLog, and a NSTimer instance is started.   Once the timer is started, a success callback will be sent back to the HTML/JavaScript layer.

Now, the timer is executing indefinitely in the native code.   You will notice that in the timer handler “onTick” function, a message is written to the console, and a JavaScript string is created and routed to the UI layer using writeJavascript.   In this case, the JavaScript string just contains the current date as a string, to maintain simplicity.

#import "SamplePlugin.h"

@implementation SamplePlugin

- (void) start:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {

    NSString* callbackID = [arguments pop];
    NSLog(@"start invoked");

    [NSTimer scheduledTimerWithTimeInterval:1.0
                                     target:self
                                   selector:@selector(onTick:)
                                   userInfo:nil
                                    repeats:YES];

    PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString: @"OK"];
    [self writeJavascript: [pluginResult toSuccessCallbackString:callbackID]];
}

-(void)onTick:(NSTimer *)timer {

    NSLog(@"timer tick!");

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"dd-MM-yyyy HH:mm:ss"];

    NSString *dateString = [formatter stringFromDate:[NSDate date]];
    NSString *js = [NSString stringWithFormat:@"updateContent( '%@' );", dateString];

    [self writeJavascript:js];
}

@end

When the native-generated JavaScript is executed, it will invoke the updateContent() JavaScript function, which will then update the HTML user interface.

While this example is extremely basic, you could use this exact same approach to push more complex data to the UI layer in real time.  This data could be coming from network activity, Bluetooth connections, or physical devices that are connected to the mobile device.

You can download the full source code for this sample project directly from: PhoneGapPluginDataPush.zip