Sergio Panagia Blog

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”.

Choose PhoneGap template

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.

Our app powered by iScroll4

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.

12 Responses to Into PhoneGap: an example of API-aware native iPhone app with HTML5 and SQL local database

soyoh says: 8 giugno 2011 at 19:08

Hi!
Thanks for this example, I’m trying to create a login function using my own rest api, do you think its better using html databases? What thinks Apple about this? Or theres anothers solutions??

I’m a bit noob on this, sorry.
You say That its better not to use jquery, what youthink about jqtouch or jquerymobile?

Thanks,
And do you have the code on any repo?? Git or something

Rispondi
panaghia says: 9 giugno 2011 at 12:56

About the html database I assume you are asking if it’s better to user built-in SQLite db (common for native apps) or webkit local storage (like this example). Well, I suppose Apple itself implemented SQL local DB in webkit, so I don’t think that adopting this solution might represents a problem. Moreover, actually does not seem to be possible to use “native” db from PhoneGap using its API.

jQtouch and jQueryMobile have good UI libraries. The problem it’s you have to include jQuery core, that it’s quite heavy for a mobile browser. So the choice is yours, if you can’t build your own UI, consider using one of those frameworks, but except a loss of performance and ux.

Rispondi
soyoh says: 9 giugno 2011 at 22:29

Hi,
thanks for your answer, like you say, i’ll continue using javascript, its better to load not heavy libraries

do you have the code on any repository?? i think i’m missing something :/
thank you

Rispondi
New York Snow says: 17 settembre 2011 at 06:08

Very great post. I just stumbled upon your blog and wanted to mention that I have really loved surfing around your blog posts. In any case I?ll be subscribing for your feed and I am hoping you write again soon!

Rispondi
Fabrizio says: 26 novembre 2011 at 15:55

Hi, can you please post the full source of the files you created? I am getting lost at a couple of places, no knowing if you are referring to different html files, or where in index.html this pieces of code are supposed to go.
Thanks, Fabrizio

Rispondi

Lascia un Commento

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

*

*

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>