Building a native mobile app with Titanium in less than an hour

Posted: July 13th, 2010 | Author: | Filed under: Mobile, Tutorials | 24 Comments »

UPDATE: 24 Nov 2010
Please note I no longer develop apps using Titanium and no longer follow version updates so the examples below may no longer work.



When I wrote my last blog entry, I favoured objective-c over Appcelerator’s Titanium framework thinking it just wasn’t good enough. Almost 6 months later and I would like to take back that comment. Titanium is no longer an embedded web browser inside an app. It uses Javascript to compile an entire iPhone/Xcode project with objective-c, so it uses native UI elements using clever APIs. This is also true for Android apps, iPad and now Blackberry.

This is not to say there’s no need to use Xcode ever again. But if you’re planning to build a general navigation-based app with loads of menus, windows and the ability to pull/push data from the web, it can be done much faster with Titanium. Plus you’ll be able to create the same app for multiple platforms without re-writing the code.

So I wanted to put together a relatively simple app that lets me browse and search through a list of items, in this example, animals; and be able to group them alphabetically like the iPhone’s contacts directory. Then I want to click on an item to see more details and be able to save my favourite items for easy access later.

I’ve created a sample app that you can download and use right now. All you have to do is add your own data and you’re done.

I’m going to quickly run you through how to set up the tabs, the list, alphabetical grouping, search and detail view for each item. I won’t explain the saving feature here but once you do the main part below you should be able to easily follow the rest by looking at the code.

Here’s how it works. See the sample app to work out where to place individual files.

1. Create a tab group. One for the main list and one tab for saved items:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// create tab group
var tabGroup = Titanium.UI.createTabGroup();
 
// create base UI tab and root window
var mainList = Titanium.UI.createWindow({
    title:'Animals',
    backgroundColor:'#ffffff',
    barColor:'#333',
    url:'mainList.js',
    tabBarHidden: false
});
var tabMainList = Titanium.UI.createTab({
    icon:'book.png',
    title:'Animals',
    window:mainList
});
 
// create saved items tab
var faves = Titanium.UI.createWindow({
    title:'Saved Items',
    backgroundColor:'#ffffff',
    barColor:'#333',
    url:'faves.js',
    tabBarHidden: false
});
var tabFaves = Titanium.UI.createTab({
    icon:'star.png',
    title:'Saved Items',
    window:faves
});
 
//  add tabs
tabGroup.addTab(tabMainList);
tabGroup.addTab(tabFaves);
tabGroup.open(tabMainList);
 
Titanium.UI.currentWindow.add(tabGroup);

That’s it. We have now created two tabs. mainList.js will take care of the content for the first tab and faves.js for the saved items tab.

2. Create a JSON doc (data.js) containing your data for the list:

1
2
3
4
5
6
7
var myData = [
    {"title":"Armadillo", "description":"Armadillos eat ants but they are not Anteaters"},
    {"title":"Bear", "description":"Bears are awesome and they like honey"},
    {"title":"Dog", "description":"Dogs are the best and they woof"},
    {"title":"Deer", "description":"D'oh! a deer..."},
    {"title":"Zebra", "description":"Zebras are stripey and cool"}
];

3. Set up your directory list in the first tab (mainList.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// include the data
Titanium.include('data.js');
 
var data = [];
var firstLetter;
var oldFirstLetter;
 
// loop through data and add to list
for (var i = 0; i < myData.length; i++)
{
	// get first letter of each item for grouping
	firstLetter = myData[i].title.substr(0,1);
	if(i == 0){
	    oldFirstLetter = firstLetter;
	    Ti.API.info(oldFirstLetter);
	} else {
	    if(firstLetter == oldFirstLetter){
    	        oldFirstLetter = null;
    	    } else {
    	        oldFirstLetter = firstLetter;
    	    }
        }
        data.push({title:myData[i].title,hasChild:true,header:oldFirstLetter});
        oldFirstLetter = firstLetter;
}
 
// add search bar
var search = Titanium.UI.createSearchBar({
	showCancel:false
});
 
// create table view
var tableview = Titanium.UI.createTableView({
    data:data,
    search:search,
    filterAttribute:'title'
});
 
Titanium.UI.currentWindow.add(tableview);
 
// create table view event listener
tableview.addEventListener('click', function(e)
{
    var win = Titanium.UI.createWindow({
		url:'details.js',
		backgroundColor:'#ffffff',
                barColor:'#333333',
		title:e.rowData.title
	});
 
	// send selected item's title to detail page
	win.currentItem = e.rowData.title;
 
	Titanium.UI.currentTab.open(win,{animated:true});
});

Notice in the tableview event listener we are telling it to just open a new window on click and pass the item’s title.

4. Display the selected item’s data in details.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// include the data
Titanium.include('data.js');
 
win = Titanium.UI.currentWindow;
 
var desc = '';
 
// create scroll view for the content
var scrollView = Titanium.UI.createScrollView({
	contentWidth:'auto',
	contentHeight:'auto',
	top:0,
	showVerticalScrollIndicator:true,
	showHorizontalScrollIndicator:false
});
 
 
// loop through data and get correct item by title (this is easier than using Id's incase you add new items later)
for (var i = 0; i < myData.length; i++){
    if(myData[i].title == win.currentItem){
        desc = myData[i].description;
    }
}
 
// create description label
var label = Titanium.UI.createLabel({
	text:desc,
	height:'auto',
	width:300,
	top:10,
	font:{fontSize:16},
	color:'#333333',
	textAlign:'left'
});

That’s it. I said an hour to develop this but in reality it will probably take you much less once you get used to the APIs. Of course for the data source you can use XHR to get data from the web but if you want your app to be usable without an internet connection, you would have to store the data locally like this.

I hope this was helpful. Let me know if you have any questions.

24 Comments »
  • http://Website Zi

    Hey, thanks. Explaining the save part would be useful.

  • http://www.givp.org Giv

    Hi Zi,
    No problem. I’ll do a follow-up post about saving next.

  • http://Website Nicholas

    hey giv thanks for the code but i would like to ask why does the header group it in twos instead of everything belonging to A together.

  • http://www.givp.org Giv

    Hi Nicholas. I’m not sure what you mean? the code should group them alphabetically. if there are 3 items starting with A then they would go under the A header etc. Or maybe I’ve misunderstood your question?

  • http://www.givp.org Giv

    Sorry Nicholas, I just noticed your other comments. It somehow got flagged as spam :(

  • http://www.givp.org Giv

    Hmm, it’s strange. I see what you mean now. There must be a problem with the logic in the loop that groups items. Let me look at the code and get back you.

  • http://Website Nicholas

    ok thanks giv, hope to hear from u soon.

  • http://www.givp.org Giv

    Hi Nicholas, I see the problem. I’m guessing your list is not already sorted alphabetically? My script assumes it’s all from A to Z. I think sorting it in Titanium creates a lot of extra code.

  • http://Website Nicholas

    Hi Giv, my list is already sorted alphabetically. erm, i did dl ur application and tried adding more datas to your data.js and sorted them alphabetically. it still gives me the same problem. sry to disturb u.

  • http://Website Nicholas

    http://pastie.org/1085532 – the code.
    http://pastie.org/1085542 – xml file. i have sorted it alphabetically b4 the start of my coding.

  • http://www.givp.org Giv

    Hi Nicholas,

    I’ve changed the grouping logic and now it should all work correctly. I’m sure there’s a neater way to do it but this works.

    Thank you very much for pointing out this bug. I’ve updated the code in this post and will update the code in Github shortly.

  • http://Website Nicholas

    Hi Giv, thanks for the help! appreciate it a lot!

  • http://Website Tristan

    I’d love to give Titanium a go, but I can’t get it to install. The installer crashes, or the Titanium Developer app crashes, or the iPhone simulator never opens (no errors though).

    Is this really a stable platform? I’m not convinced. The Apple tools seem great in comparison, and while it’s hard to resist the idea that I can click my fingers and have a decent app, I find it hard to believe. I would love to be proved wrong though :)

  • Giv

    I have to say I’ve moved-on from Titanium. It’s a good concept and works for little things but if you’re doing anything serious, I would invest some time into learning Objective-C. Let’s be honest, Titanium is a hack and Appcelerator don’t work directly with Apple so whenever Apple release an OS update, things in Titanium inevitably break. It takes them a while to figure out the problem and fix.

  • http://ranjithtenz.wordpress.com Ranjith Kumar

    Hi,
    I`m new to Titanium I would like to know how to create calculator application for android using titanium.

  • Giv

    Heya,

    It should be pretty easy. Why don’t you ask the community here: http://developer.appcelerator.com/questions

  • http://Website Carl

    Hi, when I try to install it, it crashes. I have the latest sdk (4.1). Suggestions?

  • Giv

    Hi Carl,

    Does Ti crash for any app or just my sample app? Do you get anything in the logs?

  • http://Website Carl

    Hi!
    Ti does not crash with other apps…
    here´s my log.
    Cheers,
    Carl

    [ERROR] The application has crashed with an unhandled exception. Stack trace:
    0 CoreFoundation 0x03844b7c __exceptionPreprocess + 156
    1 libobjc.A.dylib 0x0399440e objc_exception_throw + 47
    2 CoreFoundation 0x038466ab -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3 CoreFoundation 0x037b62b6 ___forwarding___ + 966
    4 CoreFoundation 0x037b5e72 _CF_forwarding_prep_0 + 50
    5 UIKit 0x008dd8a1 -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:] + 315
    6 UIKit 0x00a25c62 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 116
    7 UIKit 0x00a2535f -[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 327
    8 UIKit 0x00a282e0 -[UITableViewRowData rectForFooterInSection:] + 151
    9 UIKit 0x00a27b41 -[UITableViewRowData heightForTable] + 60
    10 UIKit 0x008e88fe -[UITableView(_UITableViewPrivate) _updateContentSize] + 333
    11 UIKit 0x008d7eae -[UITableView noteNumberOfRowsChanged] + 123
    12 UIKit 0x008e453c -[UITableView reloadData] + 773
    13 parva 0x000798a1 -[TiUITableView reloadDataFromCount:toCount:animation:] + 173
    14 parva 0x0007a08e -[TiUITableView replaceData:] + 1463
    15 parva 0x0007b1bd -[TiUITableView dispatchAction:] + 2381
    16 Foundation 0x005ec3ca __NSThreadPerformPerform + 251
    17 CoreFoundation 0x03825faf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    18 CoreFoundation 0x0378439b __CFRunLoopDoSources0 + 571
    19 CoreFoundation 0×03783896 __CFRunLoopRun + 470
    20 CoreFoundation 0×03783350 CFRunLoopRunSpecific + 208
    21 CoreFoundation 0×03783271 CFRunLoopRunInMode + 97
    22 GraphicsServices 0x0542b00c GSEventRunModal + 217
    23 GraphicsServices 0x0542b0d1 GSEventRun + 115
    24 UIKit 0x0087baf2 UIApplicationMain + 1160
    25 parva 0×00003569 main + 362
    26 parva 0×00002351 start + 53
    2010-11-23 19:26:25.476 parva[78046:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[NSNull length]: unrecognized selector sent to instance 0x38aed68′
    *** Call stack at first throw:
    (
    0 CoreFoundation 0x03844b99 __exceptionPreprocess + 185
    1 libobjc.A.dylib 0x0399440e objc_exception_throw + 47
    2 CoreFoundation 0x038466ab -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3 CoreFoundation 0x037b62b6 ___forwarding___ + 966
    4 CoreFoundation 0x037b5e72 _CF_forwarding_prep_0 + 50
    5 UIKit 0x008dd8a1 -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:] + 315
    6 UIKit 0x00a25c62 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 116
    7 UIKit 0x00a2535f -[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 327
    8 UIKit 0x00a282e0 -[UITableViewRowData rectForFooterInSection:] + 151
    9 UIKit 0x00a27b41 -[UITableViewRowData heightForTable] + 60
    10 UIKit 0x008e88fe -[UITableView(_UITableViewPrivate) _updateContentSize] + 333
    11 UIKit 0x008d7eae -[UITableView noteNumberOfRowsChanged] + 123
    12 UIKit 0x008e453c -[UITableView reloadData] + 773
    13 parva 0x000798a1 -[TiUITableView reloadDataFromCount:toCount:animation:] + 173
    14 parva 0x0007a08e -[TiUITableView replaceData:] + 1463
    15 parva 0x0007b1bd -[TiUITableView dispatchAction:] + 2381
    16 Foundation 0x005ec3ca __NSThreadPerformPerform + 251
    17 CoreFoundation 0x03825faf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    18 CoreFoundation 0x0378439b __CFRunLoopDoSources0 + 571
    19 CoreFoundation 0×03783896 __CFRunLoopRun + 470
    20 CoreFoundation 0×03783350 CFRunLoopRunSpecific + 208
    21 CoreFoundation 0×03783271 CFRunLoopRunInMode + 97
    22 GraphicsServices 0x0542b00c GSEventRunModal + 217
    23 GraphicsServices 0x0542b0d1 GSEventRun + 115
    24 UIKit 0x0087baf2 UIApplicationMain + 1160
    25 parva 0×00003569 main + 362
    26 parva 0×00002351 start + 53
    )
    terminate called after throwing an instance of ‘NSException’

  • Giv

    Hi Carl,

    Sorry, I’m not sure. It’s possible that Ti has changed since this was written. Have you tried using an older version?

    btw. this is one of the reasons why I have stopped using Titanium. Whenever a new version is released, a lot of my apps were breaking so I decided to bite the bullet and just learn Objective-C and focus on iOS development only.

  • http://Website prashant solanki

    http://pastie.org/1085532
    hay nicholas i refer this link and it helps me alot thanks for upload this link
    but i have minor query that in this link the code contain Definitions.js in xml code so i want to know that what code is in the Definitions.js should be so that my apps can run successfully

  • http://Website pranay

    Hi Giv,
    I downloaded your codes but every time i am launching its showing an error,i am using titanium SDK 1.5.1.
    I ma not able to send you the screen shots,if u could give me your email id,i’ll be able to,BTW the error i am getting says illegal character at[0,3]app://mainList.js

  • Giv

    Hi Pranay,

    Please see the note on top of this page. I no longer maintain this code so it may not work with the new versions of Titanium. But the error sounds like you aren’t using plain text maybe? there may be non-ascii characters in there. Just a guess.

  • André

    It took me 1 day to find out, how to make this (great) script running. Instead of writing:
    oldFirstLetter = null;

    write:

    oldFirstLetter = ”;