Sencha Inc. | HTML5 Apps

Enhancing iOS Sencha Touch apps with NimbleKit, pt II

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

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

In the second part of this NimbleKit tutorial, we look at how we can create an web-based internet radio application, and hook our Sencha Touch app up to the iOS camera and email functionality. Return to part one if you missed it.

Web App Radio

For the remaining part of this whistle-stop tour of NimbleKit's class library, let's take a look at its ability to let a web application access native device capabilities - generally an ability not granted to apps running in a regular browser environment.

Let's start with a quick and easy - but quite impressive - example. We should be able to add an internet radio player to our web app. At the start of the application code, instantiate a radio player from the NKInternetPlayer class:

var radio = new NKInternetPlayer();
radio.forceDecoderFormat("aac");

(In turns out that the player is a singleton, so you could in theory 'instantiate' it multiple times to get the reference to the single actual player.) To host the controls for our radio station, add a new tab to the application, and provide two buttons to 'play' and 'stop' the music.

items: [{
    title: 'Radio',
    layout: {type: 'vbox', align:'center'},
    defaults: {xtype: 'button'},
    items: [{
        text: 'Play',
        ui: 'confirm',
        listeners: {tap: function() {
            radio.playFromURL("http://mp1-gs130.somafm.com:80");
        }}
    }, {
        text: 'Stop',
        ui: 'decline',
        listeners: {tap: function() {
            radio.stop();
        }}
    }]
}, {...

These two buttons have very simple 'tap' handlers to play the music (from an AAC stream URL in this case), and stop it, respectively. Fire it up on your device to try it - and have some relaxing Soma FM piped gently to your web app. (In fact the iOS simulator also plays audio equally well, but it's not quite as cool to show your friends). Notice of course that you can navigate around inside the app, and the music will continue playing.

Needless to say, this might be a good opportunity to use the NKIsInternetAvailableViaCellularNetwork and NKIsInternetAvailableViaWifi() methods to choose an appropriate stream, based on connectivity. High bit-rate streams might struggle on a cellular network.

The player exposes a number of useful methods that allow you to shuffle and query the play position as well as alter the volume. Callbacks can also be provided for when the player starts and stops playing - which you can use to add a loading mask to your app while the users waits for the buffering of the stream, for example. But for this simple demo, let's just add a sliding volume control to the top of the panel, and use its drag event to set the player's volume in percent:

{
    xtype: 'sliderfield',
    label: 'Volume',
    value: 50,
    minValue: 0,
    maxValue: 100,
    listeners: {drag: function(slider, thumb, value) {
        radio.setVolume(value);
    }}
}

Also, for some streams, there's a 'meta' string that provides information about what track is playing. The player exposes this via the getCurrentMetaString method. We can create a toolbar for our page that is updated every five seconds with the track name extracted from that string:

var radioToolbar = new Ext.Toolbar({title:'Soma FM'});
 
items: [{
    title: 'Radio',
    dockedItems: [radioToolbar],
    ...
 
setInterval(function () {
    var meta = radio.getCurrentMetaString().match(/'[^']*'/);
    if (meta) {
        radioToolbar.setTitle(meta);
    }
}, 5000);

So, we now have a plain-looking, but quite fun, internet radio player built right into our app.

(Incidentally, if you need a file-based player for local resources - either audio or video - NimbleKit provides very similar NKAudioPlayer and NKVideoPlayer classes respectively.)

The code at this point is available as the 4_web_app_radio branch of the project in GitHub.

Camera Access

Other native APIs that NimbleKit can access include those that let your application access the file system, download files asynchronously, query local SQLite databases, interact with the device's clipboard, and prompt the user to send SMS messages from a modal user window. These are all simple enough to use, and the NimbleKit documentation covers them adequately.

For our finale here, though, let us show how we can access the device's camera, and then access the device's native email client to send an image from within our web application itself.

The important function here is NKPickImageObject, which we can use to prompt the user to take a photo (or select one from a previous gallery). Note that the way this API is structured does not allow us to get JavaScript access to the camera stream live - so no augmented reality web apps for now! - but it is extremely simple to use, as we shall see.

Let's add a new tab within our Sencha Touch user-interface to host this part of the demo. It contains two buttons, the second of which is initially disabled, and a panel upon which we will display the photo once it has been taken:

var photoPanel = new Ext.Panel({margin:10});
var emailButton = new Ext.Button({
    text: 'Email photo',
    disabled: true,
    listeners: {tap: function() {
    }}
})
var takeButton = new Ext.Button({
    text: 'Take Photo',
    ui: 'confirm',
    listeners: {tap: function() {
    }}
});
 
...
    items: [{
        title: 'Camera',
        layout: {type: 'vbox', align:'center'},
        items: [takeButton, emailButton, photoPanel]
    }, ...

So far, so simple:

Within the tap handlers, let's define the behavior we want. The first button invokes the camera, waits for the user to take a snap, and then returns an instance of the NKImage class to a callback that we provide, as usual specified by its string name, and the desired source of the image. In the takeButton's tap function, we add:

NKPickImageObject('takenPhoto', 'camera');

And of course we need to add the global function takenPhoto too:

window.takenPhoto = function(image) {
    alert('photo taken');
}

If you try it out on the iOS simulator, you'll possibly be confronted with a selector to ask you to pick an image from the gallery, for the simple reason that there's no camera. But on a real development device, you'll be given the camera UI, and once the image is taken, the option to 'Retake' or 'Use' the photo. Assuming you finish this workflow with the 'Use' action, the callback is fired and the NKImage object is made available to the function.

Let's do something more useful with the image than just alerting the user that we got one. NKImage provides a few useful functions, such as getters and setters for size, and in particular getBase64, which we can use to serialize it to Base64. With that encoding, this will then allow us to display it on our page through the use of a data URI:

window.takenPhoto = function(image) {
    var dimensions = image.getSize().split(',');
    var width = 200;
    var height = width * dimensions[1] / dimensions[0];
    var base64 = image.getBase64(0.6);
    photoPanel.update(
        '<img src="data:image/jpg;base64,' + base64 + '" ' +
        ' width="' + width + '" height="' + height + '" />'
    );
    photoPanel.image = image;
    emailButton.setDisabled(false);
}

The getBase64 function takes a single argument to indicate the JPEG compression of the image you'd like. Here we've set it quite low so that the sheer size of the Base64 encoded string does not overwhelm the browser. We use a standard data URI syntax to place it into the image element.

You'll notice that we calculate the dimensions of a smaller version of the image, but we don't actually resize it: relying on the browser to resize the image with the explicit width and height attributes on the img tag written into the photoPanel panel. This is for two reasons: firstly we want to keep the image at its original resolution in case the user wants to email it in its largest form. And secondly, when image.setSize(width, height) is added to the function above, the image sometimes gets strangely stretched and rotated, depending on the orientation of the camera when the photo was taken. (However, this issue was experienced on beta versions of iOS and NimbleKit, so it may be worth trying for yourself on stable versions of the platforms.)

At the end of the function you'll see we do two final things. Firstly we create a reference to the NKImage instance on the photoPanel component (so we can access it again in original form for the emailing process), and enable the emailButton so that the user can forward the photo on.

This is also very easy to do programmatically, and we attach the following code to the button's tap handler:

listeners: {tap: function() {
    var composer = new NKMailComposer();
    composer.setRecipient("my.friend@example.com");
    composer.setSubject("A Sencha Touch photo");
    composer.setBody("It's amazing what you can do with web apps these days");
    composer.addImage(photoPanel.image);
    composer.show();
}}

By now this will be completely self-explanatory. We create and configure the email composer instance, including suggested subject and recipient, add the reference to the image, and then show it.

(Again, there is a rotational artefact in this screen shot.)

When the user finally comes to send the image, they are, as usual given the opportunity to resize the graphic to reduce the size of the email. Note that because we attached the original NKImage class (as passed via the photoPanel.image reference) the highest available resolution is the raw image taken by the camera, not the reduced quality one that we inserted into the DOM of the preview page.

And that's it. Click Send, and your image, as captured from the camera and managed by your web application, is on its way.

This final app is available as the fifth, and final, 5_camera_access branch of the project in GitHub.

Wrap-up

If you are a mobile web developer and you simply want to deploy your applications into an app store, you have several opportunities available. But if you also want to make use of some particularly interesting device APIs, then a rich native shell like NimbleKit becomes useful.

In this article, we've shown how it can be used to access device capabilities in a simple and well-documented way, allowing you to add useful and compelling features to your application, such as camera access, internet player, email composer and so on. What's more, NimbleKit provides a suite of bindings to native UI controls that can be reached via JavaScript and which, in some cases, can be used to enhance your application's user experience.

Yes, we look forward to seeing additional platform support from NimbleKit, but we certainly recommend you consider adding it to your toolbox if you are building iOS-centric Sencha Touch web apps today. Have fun!

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

6 Comments

Andrea Cammarata

3 years ago

Woww, NimbleKit seems really amazing! It is certainly a great alternative to PhoneGap.
I will try once again to do some experiment with Sencha Touch smile
@James Thanks for sharing this great tutorial!

Steve

3 years ago

Wow… Very nice, dare I say better than phonegap?  Very slick.

Andrew Hall

3 years ago

This certainly looks interesting. I have developed some apps with PhoneGap - but i will have to look into this too.

verhuren

3 years ago

I’m not really good in enhancing but I think enhancing our device is a good thing to experience new features and service as well; actually I never tried it but I think will be good.

sensation of the full body

3 years ago

thanx big man

dw

3 years ago

I get a white screen when trying to work the example files and I don’t know how to debug the errors in XCode. Can someone tell me how to see errors and debug javascript in this environment > sorry I’m new to xcode and the iphone simulator.
Thanks!

Leave a comment:

Commenting is not available in this channel entry.