Sencha Inc. | HTML5 Apps

Enhancing iOS Sencha Touch apps with NimbleKit

Published Aug 10, 2011 | James Pearce | Tutorial | Medium
Last Updated Aug 10, 2011

This Tutorial is most relevant to Sencha Touch, 1.x.

It's becoming increasingly popular to build web applications that can be packaged and distributed into app stores - and which can capitalize on API extensions to get access to native device functionality and so on. We've previously discussed using Sencha Touch in conjunction with PhoneGap, for example, but there are other options too. In this article we discuss a less well-known option called NimbleKit, which, as well as packaging and device API access, also offers a few interesting opportunities to enhance a web application's user interface with native controls.

And did we mention that we'll be making a web-based radio player too?

In this two-part tutorial, we will be using NimbleKit 1.9 and Xcode 4 (in a Mac environment, of course) to create a simple iOS application that shows these concepts in action. As we work through the tutorial, you'll be able to follow the code, as it's written, on the HelloNK project in the senchalearn account on GitHub. There's a branch corresponding to each section of the article.

Skip ahead to part two if you want to play with radios and cameras... (or leave a comment).

Installation

NimbleKit for iOS is available as a DMG install, and is free to download and develop with. You must pay a small licence fee to deploy your applications onto real devices - but the free version allows you to run your app on the iOS simulator until then. The install is a typical, and easy, PKG-based process, and is essentially just the installation of Xcode templates.

Once installed, and much like PhoneGap, NimbleKit adds a new 'New Xcode Project' icon to Xcode, which you should double-click to create the native part of your hybrid application structure. You'll be prompted for some basic information about your project and a new folder location in which to save it. For this tutorial, we'll create a project called 'HelloNK'.

Xcode will then create your new project with the NimbleKit template and you'll see something like this:

Getting started

On the left you'll see the structure of the project, with directories for the application and its test suite. We'll be working in the former, the HelloNK directory. There are a couple of key sub-directory groups here: the most important being the one called HTML, where the web application will reside. Depending on what you specified for your target device types, you may also have iPhone and iPad directories, containing .xib files for each form factor.

You should immediately be able to run this project onto your local iOS simulator. Make sure the device platform you want to run it on is correctly set in the scheme drop-down of the toolbar, and then run. The HelloNK application will install to the simulator and run up, displaying the content of the sample main.html that's in the HTML directory:

It should be obvious what has happened here, even if you don't know Objective-C. The HelloNKAppDelegate.m file in your project instantiates a Nimble object, with an entry point and a serial number:

Nimble *nimble = [[Nimble alloc] initWithRootPage:@"main.html" window:self.window serial:@""];

The serial parameter can remain blank while you are running against a simulator. You'll need to add your own serial number here to make it run on real devices

For our Sencha Touch app, we'll be changing the entry-point to index.html - as per application architecture convention:

Nimble *nimble = [[Nimble alloc] initWithRootPage:@"index.html" window:self.window serial:@""];

(and you can now remove the main.html file from your project)

Adding Sencha Touch to the project

Add a new index.html file to the HTML directory on the filesystem. You'll then need to explicitly add this new file to the Xcode project. Right-mouse-click on the HTML folder in the left-hand navigator and select 'Add Files to "HelloNK"'.

Once added to the project, you can use Xcode itself (or your own text editor) to include the following standard Sencha Touch bootstrap markup in it:

<!DOCTYPE html>
<html>
    <head>
 
        <title>Hello NimbleKit</title>
 
        <script src="lib/touch/sencha-touch.js" type="text/javascript"></script>
        <script type="text/javascript" src="app/app.js"></script>
 
        <link href="lib/touch/resources/css/sencha-touch.css" rel="stylesheet" type="text/css" />
 
    </head>
    <body></body>
</html>

Here of course we are just linking to the Sencha Touch library, an app.js file for our own application, and the standard stylesheet.

Make sure that an empty app.js file, the library and stylesheet are placed in their respective locations, under app, lib/touch and lib/touch/resources/css respectively, and are then explicitly added to XCode, as we did above. It's important to cherry-pick these files, rather than include the Sencha Touch SDK wholesale, to keep the resulting application's size as small as possible.

We should now add something to our app.js file to bring up a simple UI framework on application startup:

new Ext.Application({
    launch: function () {
        var tabPanel = new Ext.TabPanel({
            fullscreen: true,
            dockedItems: [{
                xtype: 'toolbar',
                title: 'HelloNK'
            }],
            tabBar: {
                ui:'light'
            },
            items: [{
                title: 'One'
            }, {
                title: 'Two'
            }]
        });
    }
});

Again, this should be self-explanatory. We launch a TabPanel containing two named items, a light tab bar, and a docked toolbar at the top of the app as a whole. Run the Xcode project again, and the Sencha Touch application is now running as a native (well, hybrid) application in the simulator.

This code is available as the 1_basic_app branch of the project in GitHub. It is unlikely to win us 'New and Noteworthy' on the Apple App Store, so let's try and add some functionality to this application. Specifically, we want to add something that you wouldn't find in a regular web application. How about some native device access?

Querying the device

Much like PhoneGap, NimbleKit allows you to connect to native APIs by offering a new set of JavaScript classes and functions for your application to use. NimbleKit provides a large number of these, and prefixes them with the letters NK. A full list is made available in the framework's documentation.

At a first glance, this library may look daunting, particularly since a large number of them seem to be UI-related. We will return to those. For now, let us focus on a few non-UI classes and functions.

Firstly, we need to add a reference to the NimbleKit library into our index.html so that the library is available to our own JavaScript. The project's build process ensure that this is available at the same level in the application's file structure, so the reference is as easy as:

<!DOCTYPE html>
...
<script src="NKit.js" type="text/javascript"></script>
...

Adding this script tag constitutes passing something of a point of no return. This file itself is not available until the project is compiled, and it in turn requires the native environment to work correctly. We can no longer test out our Sencha Touch application in a desktop or regular mobile browser without this script 404ing, and all our upcoming native API calls failing. You'll find yourself pressing 'Run' in Xcode far more than 'Refresh' in your browser from now on.

Now that we have that script in place though, we can start calling some of the NK* functions to access device information not normally made available to a regular browser-based application. Let us change our tabs slightly, and add turn one of them into a 'Info' panel - upon which we can output the results of some of the basic calls:

items: [{
    title: 'Info',
    html:
        '<table>' +
            '<tr><th>Device</th><td>' +
                NKGetDeviceType() +
            '</td></tr>' +
            '<tr><th>ID</th><td>' +
                NKGetUniqueIdentifier() +
            '</td></tr>' +
            '<tr><th>Cellular</th><td>' +
                NKIsInternetAvailableViaCellularNetwork() +
            '</td></tr>' +
            '<tr><th>WiFi</th><td>' +
                NKIsInternetAvailableViaWifi() +
            '</td></tr>' +
        '</table>'
}]

We can also add a little styling to index.html to make the table look nicer:

<style>
    table {
        border-collapse: separate;
        border-spacing: 4px;
    }
    td, th {
        padding:4px;
        vertical-align:top;
    }
    th {
        background: #ddd;
        font-weight: bold;
    }
    td {
        background: #fff;
    }
</style>

There's nothing clever here: we are directly inserting the results of these various function calls into the HTML of the panel when it's instantiated. This demonstrates that the app has access to the device name, ID and current network connectivity via NK* APIs. Note that these functions only get called once: if you change the connectivity during the app's lifetime, the details on screen will not change. The results in the simulator as as follows:

Note also that the final two results appear to be mutually exclusive: if WiFi is available, it overrides the cellular flag to become zero, even if cell coverage is also available. You can see this in the screenshot from a real device, which has a cellular connection, yet still claims WiFi in preference:

It's true that device ID is a little esoteric for most applications, but hopefully you can think of cases where knowing the current network bearer would be useful. When loading JSON data, for example, you'll could easily use the NKIsInternetAvailableViaWifi() function in the server proxy of a model store - to indicate how many model instances to load at a time perhaps. It's powerful to be able to mitigate the slower network connectivity and the increased latency of a cellular network.

The code at this point is available as the 2_hardware_info branch of the project in GitHub.

Simple native UI controls

Looking through the NimbleKit function library, you'll have noticed that much of it is oriented towards the user-interface of your application. And this is what really separates NimbleKit from native shells that tend to encapsulate wrapped web views and device APIs alone.

Let's start with a few simple examples. A few top level functions, namely NKAlert and NKConfirm demonstrate the concept. These correspond to the browser's own JavaScript alert and confirm functions that you may already be familiar with. Let's start by adding a new tab to our application with a sequence of buttons on it. We'll be wiring up events to these to show each function in action:

items: [{
    title: 'Alerts',
    dockedItems: [timeToolbar],
    layout: {type: 'vbox', align:'center'},
    defaults: {xtype: 'button'},
    items: [{
        text: 'Alert',
    }, {
        text: 'Confirm',
    }, {
        text: 'NKAlert',
        ui: 'confirm',
    }, {
        text: 'NKConfirm',
        ui: 'confirm',
    }, {
        text: 'NKAlertSheet',
        ui: 'confirm',
    }]
}, {...

A little styling also ensure the buttons in our panel layout nicely:

.x-panel-body .x-button {
    margin:12px auto 0;
    width:50%;
}

For reasons that will become clear very shortly, you'll see we've also docked a toolbar instance called timeToolbar to the panel. This will contain a constantly-updating clock above the buttons. We need to instantiate it at the top of the launch function, and set it running at the bottom:

var timeToolbar = new Ext.Toolbar();
...
setInterval(function () {timeToolbar.setTitle(new Date().toLocaleTimeString());}, 1000);

No surprises here:

Let us wire up some events. The first two buttons we can use to fire classic JavaScript alert and confirm functions:

listeners: {tap: function() {
    alert('A regular JavaScript alert');
}}
...
listeners: {tap: function() {
    confirm('A regular JavaScript confirmation');
}}

Pressing the first two buttons then gives us the normal effect. Notice how the modal pop-ups have a fixed title (the file name, 'index.html'), and that the buttons always display 'OK' or 'Cancel'. On the simulator, or a device itself, you'll also notice how the clock stops while they display: the browser's UI thread blocks until you click a button to close them.

Compare these with the prompts that result from using the NKAlert and NKConfirm functions respectively:

listeners: {tap: function() {
    NKAlert('This is a...', '...NimbleKit alert');
}}
...
listeners: {tap: function() {
    NKConfirm(
        'This is a...', '...NimbleKit confirmation',
        'It rocks!', 'I love it!',
        'confirmCallback'
    );
}}

The resulting pop-ups have a custom title (the first argument) as well as text (the second argument). The confirmation prompt can also take custom text for the two buttons (the third and fourth arguments). What's more, you'll notice that the clock keeps running in the background: these prompts are being displayed asynchronously to the behavior of the browser and its timer.

But what is that fifth argument? NimbleKit expects you to specify a callback function that can act on the results of the confirmation prompt. This function receives the index of the button that was clicked by the user, so your application can act accordingly. Unfortunately, this callback function cannot be provided directly as an argument, but must be referred to by its string name. Here we specify confirmCallback and at the end of the launch function, we therefore need to define it:

window.confirmCallback = function (index) {
    NKAlert('You clicked...', '...button #' + index);
}

Our function simply echoes back the button that was clicked, using another NKAlert:

It's possible that you'll want to go beyond merely having two buttons, though, and NimbleKit helpfully provides a feature that allows you to create a more flexible confirmation interface. NKAlertSheet is similar to the Sencha Touch Ext.ActionSheet, providing a sheet that hosts an array of buttons that slides up from the bottom of the screen.

Since it requires a little more configuration, NKAlertSheet uses an instantiated class pattern, rather than a function. In the tap listener for the the last button, we can create the sheet, add the buttons, and then show() it:

listeners: {tap: function() {
    var sheet = new NKAlertSheet();
    sheet.init('confirmCallback');
    sheet.setTitle("Like this?");
    sheet.addButtonWithTitle("It rocks!");
    sheet.addButtonWithTitle("I love it!");
    sheet.addButtonWithTitle("Cancel");
    sheet.setStyle("blacktranslucent");
    sheet.setRedButtonIndex(2);
    sheet.show();
}}

The instantiation takes the callback (again, a string name of a global function), and we can set a title, three buttons, and a background style. We also indicate that the final of the three buttons (with index 2) will appear in red. When triggered, this listener displays the native alert sheet as below. (Again, you should notice that the browser continues to run behind this control.)

The code at this point is available as the 3_simple_ui branch of the project in GitHub.

As you can see, one of NimbleKit's unusual aspects is how it is able to blend native controls with web content. We've seen this with the alert pop-ups above, but you can take this further, with the use of native toolbars at the foot of the page, navigation controls at the top, and so on. Nevertheless, there is a philosophical mismatch with the 'single-page' Sencha Touch concept at this point. The tab bar, for example, requires distinct file-based HTML files per button, and can't be used to navigate around inside a single-page Sencha Touch app, even if you support hash fragment URL state in your Sencha Touch app. And of course, there are ways in which you implement many of these types of controls in a Sencha Touch application itself, using Ext.Toolbar and so on.

Similarly, NimbleKit also provides 'in-page' native controls, such as NKButton, NKBanner (for ad placement), NKTableView (for datasets), NKMapView, and NKSlider - as well as various other form widgets. These are generally even less valuable when you're creating a Sencha Touch application, since they rely on absolute positioning on the screen, making them harder to reconcile with a component-based layout approach - and are mostly analogous to many of Sencha Touch's own controls anyway. Nevertheless, they are well described in the NimbleKit documentation if you have a need for any of them.

Let's move onto part two of this tutorial, where we build a radio player into our app, and invoke the device's camera functionality.

Share this post:
Leave a reply

Written by James Pearce
James Pearce heads developer relations at Sencha. He is a technologist, writer, developer and practitioner, who has been working with the mobile web for over a decade. Previously he was the CTO at dotMobi and has a background in mobile startups, telecoms infrastructure and management consultancy. James is the creator of tinySrc, the WordPress Mobile Pack, WhitherApps, modernizr-server and confess.js, and has written books on mobile web development for both Wiley and Wrox.
Follow James on Twitter

Commenting is not available in this channel entry.