Into PhoneGap: an example of API-aware native iPhone app with HTML5 and SQL local database
These days I had the opportunity to test PhoneGap, an open source framework for creating native mobile apps with web standards.
So, first things came to mind when you think about HTML5, is that even if mobile browsers are very fast to implement new features, you cannot truly count on “standards” but on “drafts”. The consequences are worse documentation and hard-times in finding help in topics and discussions over the web.
Anyway, I wanted to test difficulties in implementing a real-world prototype for a web, mobile, application.
The object: A Tumblr dashboard reader for iOs
PhoneGap says it supports six platforms, it represents a truly advantage to products to deploy on different devices such as iOs, Android or BlackBerry.
The prototype for this example wants to test iOs environment with PhoneGap, to build a native application with two basics functionalities within Tumblr API:
- Login / Authentication system.
- Dashboard read.
To accomplish these tasks, on a common native iOs app, programmers store login credentials on local SQLite3 db. In PhoneGap there does not seem to interact this way, meanwhile we may use the webkit-powered database announced with HTML5 storage capabilities.
Xcode & setup
Installing PhoneGap on OSX is a piece of cake, after the process launch Xcode and choose “Create a New Xcode project”.
From that point, the content of WWW folder will be our working directory where we’re gonna place our files.
If you want to emulate iOs UI and behavior you should put your hands on CSS coding and consider implementing iScroll4 by Cubiq. This plugin comes in help to a problem with webkit rendering engine that does not allow position of fixed contents like top headers and bottom menus, typical of iOs apps.
So include a custom stylesheet and javascript libraries as a resources in Xcode and locate them in the www folder. If you are wondering about performance with mobile browsers, no doubt at all they are. I tested some jQtouch application on my 3G iPhone and experience was not satisfactory.
Anyway, a bit of help in JavaScript might let you save some time, so take a look at micro.js to realize what’s the library best suited for your needs. For the purposes of this prototype, xui or snackjs are good solutions.
UI & Behavior
So you need a header, a footer and a wrapper in which user may scroll content with touch interaction. For details about how to set up iScroll see Cubiq official page.
First, include your JavaScript libraries in HEAD section:
<script type="text/javascript" charset="utf-8" src="iscroll.js"></script> <script type="text/javascript" charset="utf-8" src="xui.js"></script>
Modify CSS stylesheet to emulate iOs UI elements (see full stylesheet in iScroll demo):
#header {
position:absolute; z-index:2;
top:0; left:0;
width:100%;
height:45px;
line-height:45px;
background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #96D9FF), color-stop(0.05, #189AD6), color-stop(1, #0A577A));
padding:0;
color:#eee;
font-size:20px;
text-align:center;
}
#footer {
position:absolute; z-index:2;
bottom:0; left:0;
width:100%;
height:48px;
background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(0.02, #666), color-stop(1, #222));;
padding:0;
border-top:1px solid #444;
}
#wrapper {
position:absolute; z-index:1;
top:45px; bottom:48px; left:0;
width:100%;
background:#aaa;
overflow:auto;
}
Modify CSS until your app is similar to the one in figure.
This will be the basic structure to show and inject dashboard items (posts from Tumblr) into our application. Basically an HTML LIST structure.
The first thing to do now is to add gain user credentials in order to use Tumblr API.
We said we will implement this with HTML5 database, so the alghoritm is quite simple: once app is started
- Open local database
- Try to grab (select) username and password from table “auth”
- If a an error raises (table not found) means we have no auth info on file, so create table and load a “login” section
- Else if we got data on file, let’s start with the Tumblr API request
Store email/password in webkit SQL database
First, open (or create) local database:
try
{
var shortName = 'tapp';
var version = '1.0';
var displayName = 'Tumblr App Database';
var maxSize = 65536;
db = openDatabase(shortName, version, displayName, maxSize);
}
catch(e)
{
console.log(e);
}
[1]Now, try to get user’s tumblr authentication data from this database:
try
{
db.transaction(
function(transaction)
{
transaction.executeSql('SELECT user, passwd FROM auth', [], authDataHandler, errorHandler);
});
}
catch(e)
{
alert(e.message);
return;
}
At this point, we are doing a SQL select on our previously opened db, delegating function authDataHandler to respond to success db call, errorHandler to respond if troubles occurred.
First case: table does not exists, sql transaction raises error code 1 (edit: in Safari 5.1 error code is 5. Be sure to check out the right code
errorHandler = function(transaction, error)
{
if(error.code == 1) //table auth does not exists
{
try
{
db.transaction(
function(transaction) //create auth table
{
transaction.executeSql(‘CREATE TABLE IF NOT EXISTS auth(user text primary key, passwd text);’, [], nullDataHandler, errorHandler);
//load login screen with xhr call (xui library)
x$(‘#scroller’).xhr(‘inner’, ‘login.html’);
});
}
catch(e)
{
}
}
return true;
}
At this point we load a login page with a form and (submit) event management:
<div id="login"> <form id="inputForm"> <ul> <li><input type="text" id = "username" placeholder = "username" /></li> <li><input type="password" id = "password" placeholder = "password" /></li> <li><input type="submit" id = "saveAuth" value = "Save" /> </li> </ul> </form> </div>
And attached event for submit operation:
document.getElementById('inputForm').addEventListener("submit",function(e)
{
e.preventDefault();
var email = document.getElementById('username').value;
var passwd = document.getElementById('password').value;
var req = snack.request({
method: 'post',
url: 'http://www.tumblr.com/api/authenticate',
data: {
email: email,
password: passwd
}
},function(err, res)//the callback
{
if(err)
{
err == '403' ? alert('wrong login/password.') : alert('Sorry unable to process request.');
return;
}
else
{
that.saveAuthData(email, passwd);
return;
}
});
return false;
}, false);
In this snippet of code we are listening to submit event, once triggered we grap username and password inside the form and make basic authentication request to Tumblr API service. In this code example I used Snack function to make the Ajax call. SnackJS has a really nice Request implementation similar to MooTools one.
If errors occurred, we give advice to user, otherwise login credentials are accepted and we proceed to store in the local database for further use.
In the callback section, once tested user credentials, we pass email and password to saveAuthData function: now we have to make a sql CREATE TABLE and INSERT INTO operation trough a sql-transaction:
function saveAuthData(email, passwd)
{
try
{
db.transaction(
function(transaction)
{
transaction.executeSql('CREATE TABLE IF NOT EXISTS auth(user text primary key, passwd text);', [], nullDataHandler, errorHandler);
transaction.executeSql('INSERT INTO auth(user, passwd) VALUES("'+email+'", "'+passwd+'");', [], loginNullDataHandler, errorHandler);
});
}
catch(e)
{
console.log(e.message);
}
function loginNullDataHandler()//data has been saved in db
{
location.href = "index.html";
}
}
Requesting Tumblr Dashboard via JSONP
Now we've got user auth info on local database, and we're back to index.html page. Next time user will access to our application, the process will begin from [1] getting user's authentication data from local database.
We previously declared the case in which there was no table to pull data from, we need to specify the successful one: authDataHandler()
authDataHandler = function(transaction, results)
{
//just check, for security reasons if there are data in db
if(results.rows.length == 0)
{
x$('#scroller').xhr('inner', 'login.html');
}
else if(results.rows.length > 0)
{
that.userEmail = results.rows.item(0).user;
that.userPassword = results.rows.item(0).passwd;
var params =
{
url: 'http://www.tumblr.com/api/dashboard/json',
data:{
email: that.userEmail,
password: that.userPassword
},
key: 'callback'
}
var req = snack.JSONP(params,
function(data)//the callback
{
that.getDashboard(data);
});
}
}
Once obtained user email and password from local database, we make a JSONP request via SnackJS making an authentication request for user's dashboard. In the callback function, we pass returning data to a function getDashboard that we're going to define in next lines of code; basically we will navigate trough JSON structure and fetch single posts from the dashboard making a check on the type (E.g., Audio, Photo, Regular):
function getDashboard(data)
{
for(var i=0; i< data.posts.length; i++)
{
var post = data.posts[i];
//check post type and inject content into [ul] element
else if(post.type == "regular")
{
var body = post["regular-body"];
var title = post["regular-title"];
var author = post.tumblelog["name"];
var el = document.createElement('li');
el.innerHTML = "<h1>"+author+"</h1><h2>"+title+"</h2><p>"+body+"</p>";
document.getElementById('thelist').appendChild(el);
}
else if(post.type == "photo")
{
console.log("photo");
var photoLink = post["photo-url-250"];
var el = document.createElement('li');
el.innerHTML = "<img src="+photoLink+" />";
document.getElementById('thelist').appendChild(el);
}
[..]
Next time user will launch this app, authentication will be skipped (until user won't erase mobile safari database).
The example ends here but it should be clear how to proceed in further operations in order to add new functionalities like writing a post or uploading a picture.
Conclusions
Local database is an amazing feature, with native apps wrapped into PhoneGap its potentials are event wider.
Despite usage of web standards, developing on mobile devices is not like on common desktops: you have to seriously pay much attention in performance. If you can write native apps for iPhone with HTML5 this doesn't mean it's the jQuery's amateur developer party. Developing on mobile devices is a real discriminating proof if you want to test your implementation's skills. So a few suggestions:
- Try to use native javascript as much as you can.
- Obviously avoid javascript-based animation (and count on hardware accelerated CSS transform properties).
- Avoid heavy libraries, take a look at microjs.com.
- jQuery basics functions may easily replaced with pure javascript implementations.
Example App video
Update: a live demo of a similar app is available for download.

