Titanium Mobile: Database Driven Tables with SQLite

Titanium Mobile: Database Driven Tables with SQLite

Tutorial Details
  • Technology: Titanium Mobile
  • Difficulty: Intermediate
  • Completion Time: 45 - 60 Minutes
This entry is part 1 of 3 in the series Titanium Mobile: Database Driven Tables with SQLite

This tutorial will introduce you to SQLite and database-driven development with Titanium Mobile. Specifically, you will learn to build an application for managing products with a database instead of just the file system.

Why Make a Database-Driven Application?

It makes no logical sense to have to manage an individual file for a product. Especially when there is a large product catalog to maintain. Here's an example.

File Structure Example

The folders and files on the left is how our project will look at the end. Just a few files in a folder and a database file. On the right is an out of control, daunting, directory. I feel this image should be enough motivation. One of the other benefits will be the reduced app footprint.


Prerequisites

We will need Titanium Mobile, a good text editor (anyone you are comfortable with will work), and an SQLite database manager. Here are the two SQLite managers I use:

Both of these are great options. I really like SQLite Manager, but I personally dislike that it runs as a Firefox plugin rather than a standalone application. SQLite Database Browser does not look as polished, but it can be run as a standalone application. Because of this, I tend to use SQLite Database Browser.

Step 1: Create the Application

Open Titanium Developer and create a new project. Choose Mobile and fill in all of the required information. Titanium developer automatically detects if the iPhone SDK and the Android SDK are properly installed. Then click Create Project.

New Ti Project Example

Step 2: Creating and Populating the Database

Open whichever program you choose and click on New Database. Name the database "products.sqlite". Application, database, and file names do not matter. Just remember what you use. Save the file in the Resources folder that Titanium Developer created. Fill in the database with your product information. Make sure you hit save.

Database Example

Step 3: Building app.js

Open the app's Resources/app.js file (If you have not worked with Titanium Developer before, "app.js" is the main file for your application).

Remove all of the existing code.

With simplicity in mind, I want to keep all of the files for products in a single folder. Let's go to the Resources folder and create a folder named "products". We will save our other files here.

Back in app.js, we want to create a tab group because doing so will make it easy to add more tabs and content later. Set the window label, have that window open our products file, and add the window to our current view. You will need an image for your tab image. I copied an image from the tabs in the KitchenSink app, which is the showcase demo project provided by Appcelerator to showcase all of their features.

	// create tab group
	var tabGroup = Ti.UI.createTabGroup();
	// create main window
	var main = Ti.UI.createWindow({
	    title:'Product Categories',
		url:'products/product_category.js'
	});
	// craete main tab
	var tab = Ti.UI.createTab({
	    icon:'images/tabs/KS_nav_ui.png',
	    title:'Products',
	    window:main
	});
	// add the tab to the tab group
	tabGroup.addTab(tab);
	// open tab group
	tabGroup.open();

We will not have to touch the code in app.js again, so you can go ahead and close it if you’d like.


Step 4: Building the Product Category Page

Create a new JavaScript file in our products folder named product_category.js. The important thing to keep in mind with this page is that it will be the first to load when our application initially launches. That means we are going to have the bulk of our code in this file. The great thing is that this code will be reused for our next file, with only a few changes.

Before we code this page, we should think through what needs to happen. The page needs to open a database, read the category column, and display the results in a table view. To attach the database results to the tableView, we need to create an array and push the data to it. The best way to do this is by creating a function. This way we do not have to know ahead of time how many rows in the database need to be read. We will simply let the function iterate through the database until it reads all active rows.

We also need to create an EventListener for when one of the rows is clicked. Let's start by getting everything together except the function and EventListener.

	// create var for the currentWindow
	var currentWin = Ti.UI.currentWindow;
	// set the data from the database to the array
	function setData() {
		**FUNCTION HERE**
	};
	// create table view
	var tableview = Ti.UI.createTableView({
	});
	tableview.addEventListener('click', function(e)
	{
		**EVENTLISTENER HERE**
	});
	// add the tableView to the current window
	currentWin.add(tableview);
	// call the setData function to attach the database results to the array
	setData();

This is all pretty straightforward. Now the setData() function. Start by connecting to the database and querying it for the category. To remove duplicates and only return one row for each category, we are going to use the DISTINCT SQL command.

	var db = Ti.Database.install('../products.sqlite','products');
	var rows = db.execute('SELECT DISTINCT category FROM products');

Create the array and use a while statement to iterate through the database. Query the database for the field name "category" and set that to the row title. (Titanium Developer uses "fieldByName" to read from the defined column in a database. It is used in conjunction with our query to select our content.) Specify that there is a child element, and set the path to the next file.

	var dataArray = [];
	while (rows.isValidRow())
	{
	    dataArray.push({title:'' + rows.fieldByName('category') + '', hasChild:true, path:'../products/products.js'});
	    rows.next();
	};

Finally, attach the array to our tableView.

	tableview.setData(dataArray);

Now, we need to create the EventListener to open the next page. One thing we will also need to do here is to pass the variables to the next file. This will be used to build the next SQL query and set the window title. We will also set the callback from the listener to the path to the next file, even though it is static.

	tableview.addEventListener('click', function(e)
	{
		if (e.rowData.path)
		{
			var win = Ti.UI.createWindow({
				url:e.rowData.path,
				title:e.rowData.title
			});
			var prodCat = e.rowData.title;
			win.prodCat = prodCat;
			Ti.UI.currentTab.open(win);
		}
	});

The listener catches the click, then creates a new window. This new window opens the next file and is setting the title of the new window to the content of the row. In our case it is the product's category. We then create a variable from the row content and pass it to the new window, naming the var. Lastly, the listener opens the new tab group, which is the next page.

Here is what the "product_category.js" file shows:

product_category.js Example

Step 5: Building the Products Page

Select all of the code in the "product_category.js" file and hit copy. Create a new JavaScript file named "products.js" and paste the code. We only need to make a few modifications and add the variable we passed for this page to work.

Inside our function, underneath our call to open the database, add the following code, catching out passed var.

	var prodCat = Ti.UI.currentWindow.prodCat;

Now we need to change our query to the database and use our new variable. We want to now read the product names from our previously selected category and display those products.

	var rows = db.execute('SELECT * FROM products WHERE category="' + prodCat + '"');

Change the fieldByName in the row from "category" to "name" and the path to the next file.

	dataArray.push({title:'' + rows.fieldByName('name') + '', hasChild:true, path:'../products/product_specs.js'});

The last thing needed to complete this page is to change the passed var in the EventListener. Change it to pass the product name and use a var name that makes sense.

	var prodName = e.rowData.title;
	win.prodName = prodName;

Done. Here is what "products.js" looks like:

products.js Example

Step 6: Building the Individual Product Page

This is the last file we need to create for this tutorial. The goal of this page will be to display the content of the row for the individual product. Let' look at all of the code and then break it down.

	// create var for the currentWindow
	var currentWin = Ti.UI.currentWindow;
	var db = Ti.Database.install('../products.sqlite','productSpecs');
	var prodName = Ti.UI.currentWindow.prodName;
	var rows = db.execute('SELECT * FROM products WHERE name="' + prodName + '"');
	var data = [
	{title:'' + rows.fieldByName('width') + '', header:'Width'},
	{title:'' + rows.fieldByName('height') + '', header:'Height'},
	{title:'' + rows.fieldByName('color') + '', header:'Color'},
	{title:'' + rows.fieldByName('qty') + '', header:'Quantity'}
	];
	var tableview = Ti.UI.createTableView({
		data:data
	});
	currentWin.add(tableview);

What is happing above is the same thing that we have done on the previous pages, we are just not listening for an action, and are populating the array ourselves, rather than using a function. This way we are able to label the information with a row header and order the content how we want it to display.

All that needed to be done was to create an individual row in the array using our database column names.

product_specs.js Example

Wrap Up

This seems like a lot of work in the beginning, but the result is well worth it. The best part is that once the code is complete in the first two product files, it will never need to be touched. The setData() function scales without any help. The same code will work regardless of whether the database has one row or fifty-three!

Series NavigationTitanium Mobile: Database Driven Tables with SQLite – Part 2»

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Seb

    Nice tutorial!
    What about an updating mechanism with a check for new entries online?

    Thxxxx

  • Sean

    Thank you very much. I found it very useful. What would be great is if you now showed how to update/reinstall the database remotely. I’m pretty sure i can find it elsewhere if i look, but it would be a great follow-up nonetheless.

    Thanks

  • http://www.CalvinBushor.com Calvin

    Awesome! I have been googling how to access a DB for sqlite for use on Titanium for days now and this is the best post so far. Thank you.

  • Kinsbane

    I think I’m missing something here, or something was left out. After getting blank rows in the Product Categories view, I just copied / pasted the code from the author’s file and put it in my project – and I still get blank rows for the Product Categories.

    After looking at the code, it seems weird to me – the setData method does not ask for any arguments, and indeed the last line in the file calls the method without any, but then within the setData method itself, we’re attaching the method to the tableview and passing it the dataArray, but setData doesn’t ask for arguments…

    And, the way you had laid out the code, I was getting a JavaScript compiler warning stating:
    [WARN] JavaScript compiler reported “‘tableview’ was used before it was defined.” at products/product_category.js:24

    When I moved it to below declaring the currentWin var, I had no warning. I’m just not sure why I can’t see any of the product category names.

  • Dan Stever
    Author

    Hey Kinsbane,

    I’ll answer what I know first before attempting to determine why you are getting blank rows…

    First – the warning when you are running the application. [WARN] is not something that is necessarily wrong with the code, but it is just perceived that it is incorrect by Titanium Developer (TD). TD reads the code and sees that we are using “tableview” (inside the setData function) before it was defined, but it doesn’t cause an error because it is not actually used yet. It is only used once we call the setData function. As you stated, that is the last line in the document.

    If we place the function code beneath calling it, that would eliminate this warning, but cause a fatal error because the function is then trying to execute without knowing what it is supposed to do. So, we can ignore this error.

    Second – The passing of arguments to the function. I have to take a little blame for this because I, against best practice, and without mentioning, used the same name for a function as a name for a completely separate method used by TD’s API.

    I named my function “setData” because it really is creating the array and setting the data to that array. However, “setData” is also a method of Ti.UI.TableView. (See this link for the API documentation about this: http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.TableView-object).

    So what is happening in this code ( tableview.setData(dataArray); ) is that we are specifying that our TableView named “tableview” is to use the data “dataArray” which is set by using the TD API method “setData.”

    Like I said… My bad for not stating that above AND that I created the function with the same name. Sorry for the confusion.

    Now, to try to figure out your problem. If you are not getting any rows returned, I first start thinking that your app may not be connecting to the db, or that the fieldNames are not being pulled from the db.

    My first suggestion is to strip your app code down to just installing the db, creating your SQL statement, then using an ‘alert.’ See if you get the correct value you called.

    Try that and let me know if it worked…

  • moggi

    @Kinsbane: Same here.

    In the meantime, has anybody found out what the problem is?

  • Kinsbane

    Hi Dan,

    Thanks for the comments and clarification regarding setData.

    Unfortunately I am still not able to show product category names on the category tableview – I even tried a simple “SELECT * FROM products” query and still nothing. I know it’s finding the .sqlite file just fine, because if I change anything about the path to the file itself it throws an error when I run the simulator. As I said in my previous post, I am using the exact project files offered for download with the same result as my own manually created files.

    Trying to alert out field names from the database rows within the while loop was unsuccessful – the app never launched in the simulator. Trying to do something as simple as alert(rows.fieldCount); does not allow the app to launch in the simulator. I can’t seem to see what’s going on between the database opening / executing the SQL and what, if anything, it’s actually returning.

  • Dan Stever
    Author

    Hey Kisbane and moggi,

    I’m trying to figure this out for you. Try putting the following code inside the setData function, underneath the db.execute(SQL Statement)

    var dbCat = rows.fieldByName(‘category’);
    alert(dbCat);

    Above what we are doing is creating a variable and attaching the results from the database category column. Then we are alerting that variable out. When I do this I get a single alert returning “Long.” Which is what I expected because it is the first result returned.

    If this alerts, we know that the problem is in setting the array to the tableView.

    I’m not sure if this would be an issue, but just for comparison sake, I am using Titanium Developer 1.2.2, mobileSDK 1.5.0 and the iPhone sdk 4.2.

    Try this… We’ll regroup and see where to go.

  • foroctfralion

    I love the tutorial! I have it up and working as described (with my data) but I want to change the layout of the product page. Basically I want to show an image of the product on the left and have the product name on the right. Just trying to figure out how you would handle a non liner display.

    Thanks.

    • Dan Stever
      Author

      Oh foroctfralion… You are jumping ahead. ;)

      Watch over the next week and we will be addressing JUST that. This is the kind of quality content that you get only on mobile.tutsplus.com!

      That’s the tease. Keep reading and thanks for the feedback!

  • pranay anand

    Hi,
    need u=your little help here,
    (this is the code to install the DB)

    var db = Ti.Database.install(‘../products.sqlite’,'productSpecs’);

    now what i wanna know is in [('../products.sqlite'],the dots are the path so what should i write??like the complete path as if (D:\titanium applications\LoginTest\Resources/’products.sqlite’,'products’)
    or just (Resources/’products.sqlite’,'products’) ???
    kiindly help…!!!!

  • Nick

    Saved me a lot of time, thanks! Have you posted about styling the tab and window views yet?

  • Jim

    Thanks for the tutorial, very helpful.

    How would I install multiple tables, or all tables from the db?

    Thanks again!

  • Rich

    // var currentWin = Ti.UI.currentWindow;

    There does not appear to be an object called currentWindow

  • Rohit

    I make all changes in my applicxation but after that it will not worked. ie it not picked data from database……………please help me

    var db = Ti.Database.install(‘../products.sqlite’,'productSpecs’); it will not work …………

    • Aas

      Use ‘Ti.Database.install(dblocation,dbname)’ once at the beginning of your app. You could put that line somewhere at the beginning of your app.js file.

      When you want to make a connection to the db you can then use ‘Ti.Database.open(dbname)’ every time. A

  • Norm

    Thanks for the tutorial. I’ve just started fooling around with Titanium. The SQLite db functionality is important to me, so this really helps.

    I did notice that LogCat was throwing errors that that database is not being closed. How do you close a database in Titanium? I don’t see any mention of it in the API reference.

    Thanks

  • Olaf

    Very nice Tutorial for the Apps Developer that are begining!!!!ç

    CONGRATULATIONS and THANKS

  • http://ilanperez.com Ilan

    I have the same problem as many others.

    I have copied your source files into my directory and the app keep auto closing.

    i think i have an issue connecting to the db.

    can someone put some basic code to test a db connection

    • ilan

      got it to work…

  • bhagyashree

    Hi,
    Thanks
    It really works :)
    Hey what about for updating the table list?

  • Joey Z

    Great into, I just started with Titanium. A couple questions though. Im using Titanium Studio and MySQL. Now everything is by the letter here but for whatever reason its just straight crashing out. It will load the view but it seems like it gets to a certain point and crashes the whole iPhone simulator.
    Any ideas or has anyone had the same prob?

  • Jason

    Not work on my device (Android 2.2 HTC Desire )
    Works fine in emulator!

    When starting the app I get the Appcelerator splash screen and then the error:
    The application Dynamic Products (process com.danstever.dynamicProducts) has stopped unexpectedly. Please try again.

    Same message every time, I have 21MB space available. Looks like the debugger is only for the emulator, how do we troubleshoot for the device?

  • Max

    Thank you very much Dan,
    this tutorial is awesome and it helps me a lot! :)

  • vishnu

    Any Idea? i got this error:

    D/KrollContext( 299): (kroll$2: app://products/product_category.js) [1045,1639] Running evaluated script: app://products/product_category.js
    I/Database( 299): sqlite returned: error code = 1, msg = no such table: products
    E/TiDB ( 299): (kroll$2: app://products/product_category.js) [179,1818] Error executing sql: no such table: products: , while compiling: SELECT DISTINCT category FROM products
    E/TiDB ( 299): android.database.sqlite.SQLiteException: no such table: products: , while compiling: SELECT DISTINCT category FROM products
    E/TiDB ( 299): at android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
    E/TiDB ( 299): at android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
    E/TiDB ( 299): at android.database.sqlite.SQLiteCompiledSql.(SQLiteCompiledSql.java:64)
    E/TiDB ( 299): at android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:80)

    • Siva

      Yes, I got the same error, while trying to emulate Android. I think the sqlite database is not getting copied to the emulater. Should we do something else which is missing ? Any help is really appreciated.

      Happiness ALawys
      Siva

  • Harshad Shivankar

    Thanks a ton for this tutorial..!! I m trying user authentication form and this has helped me a lot for connection with database..
    But i m facing one issue..i m able to retrieve the values from the database while using android simulator. but when i use iphone simulator, it gives me error “The operation couldn’t be completed.(Cocoa error 260)”.
    Code i m using is as follows :

    function setData(){
    var db = Ti.Database.install(‘../products.sqlite’,'products’);
    var rows = db.execute(‘SELECT * FROM login’);
    var dataArray = [];
    while (rows.isValidRow())
    {
    Ti.API.info(“Retrieving”);
    uname = rows.fieldByName(‘Username’);
    pwd = rows.fieldByName(‘Password’);
    Ti.API.info(uname + ‘ ‘ + pwd);
    rows.next();
    }
    db.close();
    }

    Please guide me through this..

  • chris

    need to do this with a sqlite database but not familar with Titanium or javascript. Is the tutorial the same with the most recent version of Titanium? ALso, Is it possible to just modify the contents of the Souce files to my own

    thx
    Chris

  • JohnnyFairPlay

    If your trying this tutorial on windows box and you get a database error, it get resolved by doing the following.

    replace:
    var db = Ti.Database.install(‘../products.sqlite’,'products’);

    with:

    var db = Ti.Database.install(‘../products.sqlite’,'products.sqlite’);

    Only three days of banging my head against the wall!!

  • Mario

    If someone has problems on opening the individual products page i changed the product.js Event listener with this

    tableview.addEventListener(‘click’, function(e){
    if (e.rowData.path){
    var win = Ti.UI.createWindow({
    url:e.rowData.path,
    title:e.rowData.title
    });
    var tab1 = Titanium.UI.createTab({
    icon:’images/tabs/KS_nav_ui.png’,
    title:’Products’,
    window:win
    });
    var prodName = e.rowData.title;
    win.prodName = prodName;
    tab1.open(win,{animated:true});
    }
    });