IOS | Titanium Development

All posts in iOS

App.Properties vs SQLite in Appcelerator Titanium Mobile: Learning to love your inner JSON

When I started working with Appcelerator Titanium, one of the big advantages was the built in SQLite database. This was a must in my first app – an RSS reader. I was working with thousands of articles in that needed to be queried and sorted in different ways. However, now that I am working in smaller apps, SQLite may not be the right solution.

In our current app, I am querying a web service and storing the data locally for off-line use. The local storage is minimal – only about 100 of the most recent records. Each record has about 8 fields. Initially, I setup database tables that mirrored the server. The onload method of httpClient request inserted the rows received from the server. The UI wasn’t as responsive as I liked while the database was being updated so I looked at some different options.

The amount of data I needed to store was small – only about 70k stringified JSON. Could I just use App.Properties to save it?

I ran some tests to compare the performance of save and load using App.Properties vs SQLite. Here are the results on different platforms:

App.Properties vs SQLite Save

App.Properties vs SQLite Load

The big difference is the in save – saving one stringified JSON object with App.Properties was 80x faster than inserting the 105 rows! The load was also much faster – 3x faster. This is probably due to the overhead creating tableViewRows when loading the table data.

Update Jan 27 2010: I have since found out that using BEGIN / COMMIT statements, I could have made my INSERT / Save SQLite code much quicker. See 10x Faster INSERTs in SQLite using BEGIN / COMMIT in Appcelerator Titanium Mobile.

Besides the speed benefit, the code is much simpiler when using App.Properties for both the save and the load.

Of course, your results may very depending on the amount of data (and if you need to sort or query), but App.Properties could save you (and your users’ iPhone) a ton of time.

Date posted: October 30, 2011

2 comments - Latest by:
  • jeff I think the App.Properties is better suited for smaller pieces of information. I suspect that parsing anything very big will ...
  • Ben Hi there, thanks for the tips, but do you know what's the storage limitation for properties? 10M? Thanks.
Top

Improving perceived performance with a large number of image views in Titanium Mobile

Last week I gave a presentation on Appcelerator Titanium to the Melbourne Cocoaheads group. These are serious iOS developers so I didn’t expect to make any converts.

After I presented, one developer came up and asked to see an app created in Titanium. I showed him the app I did for the ‘What Should I Wear’ website. After the app opened, I tapped on my second tab. The window associated with the tab had a gallery of 24 image views with ‘bigish’ images. After I tapped, it took about 2-3 seconds for the window to fully appear. It looked like nothing was happening in the app while the window was appearing.

“That’s not a great first impression” he said.

He was right. Building apps isn’t just about providing functionality – it is about providing a great user experience. In the iOS simulator you couldn’t tell there was a lag, but on the device you could really feel the delay.

I set out this week to figure out how to make the app more responsive when it is first opened. I tried four different things.

  1. Initially, I had the window in its own js file. I set the image property on all the image views in the base of the js file. When tapping on the tab, the window title appeared right away, but the area in the window was blank. Then all the images appeared all at once after a couple seconds. Not good.
  2. Using what I leaned in ‘forging titanum’, I refactored the app to a single context. I still set the image property when creating the window. When tapping the tab now, nothing happened for 2-3 seconds. Then the window appeared with all the images loaded. This was worse!
  3. I stayed with the single context, but moved setting the image properties to the ‘open’ event of the window. The window appeared quickly, then the images started appearing quickly. Not all at once, but down the page.  This looked and felt good.
  4. This time, I set the image property on the ‘focus’ event of the window. This behaved almost the same as the previous example, but seemed a little quicker. It will also provide more options when I update the image properties in the background. This is what I will use in the next version of my app.

Note: This is only testing the first time the window was opened. In every example, the window and images instantaneously appeared subsequent times you tapped on the tab. Also, I am targeting iOS only so that is all I tested.

Here is a video of the four different methods on a 3GS from a sample app I created. I added a sound effect when the ‘tap’ occurs.

As a developer, there are always different ways of doing things.  Only through testing (on the actually device) can you really polish an app to make if feel the best for the user.

Date posted: September 26, 2011

0 Comments
Top

Quick Code: debugging iPad performance and memory usage for Titanium Mobile

I’ve had some problems with crashes in My Newspaper for iPad – probably due to my over use of WebViews. I am going through the App now and moving to more native controls, but how to I determine if it is actually helping?

I created a tracer library that allows me to see how long things are taking (as device performance differs greatly to simulator performance) as well as memory usage. I wanted something with low overhead that was more ‘Log4j’ like than the Titanium API.xxxx console printings. This allows me to debug the App on device though the console in XCode.


var tracer = {}; 
(function() {

	tracer.levels = {};
	tracer.levels.DEBUG = 1;
	tracer.levels.INFO = 2;
	tracer.levels.WARN = 3;
	tracer.levels.ERROR = 4;
	tracer.levels.OFF = 5;
	tracer.allTracers = [];

	tracer.createTracer = function(name) {

		var t = {};
		t.name = name;
		
		t.startTimer = function(){
		  t.startTime = (new Date()).valueOf();
		};
		
		t.resetMemory = function(){
		 t.startMem = Ti.Platform.availableMemory;
		};
		
		t.startTimer();
	    t.resetMemory();
	    
		function makeMsg( msg ){
	      var dateTemp = new Date();
          var elapsed = dateTemp.valueOf() - t.startTime;
          var currMem = Ti.Platform.availableMemory;
          var avilableMem = Math.round(currMem) + 'mb';
          var deltaMem = Math.round(t.startMem - currMem) + 'mb';
          return( t.name  + "\t" + elapsed + "\t" + avilableMem + "\t" + deltaMem + "\t" + msg );
		};
		
		t.level = tracer.levels.INFO;
		
		t.setLevel = function(level) {
			t.level = level;
		};

		t.debug = function(msg) {
			if( t.level <= tracer.levels.DEBUG ){
			  Ti.API.debug(makeMsg(msg));	
			}
		};

		t.info = function(msg) {
			if( t.level <= tracer.levels.INFO ){
			  Ti.API.info(makeMsg(msg));	
			}
		};

		t.warn = function(msg) {
			if( t.level <= tracer.levels.WARN ){
			  Ti.API.warn(makeMsg(msg));	
			}
		};
		
		t.error = function(msg) {
			if( t.level <= tracer.levels.ERROR ){
			  Ti.API.error(makeMsg(msg));	
			}
		};

        tracer.allTracers.push(t);
        
        return t;

	};
	
	tracer.removeTracer = function( t ){
		var id = tracer.allTracers.indexOf( t );
		if( id > -1 ){
			tracer.allTracers.splice( id , 1 );
		}
	};
	
	tracer.UI = {};
	
})();

// Example Usage:
var exampleTracer = tracer.createTracer('example');
exampleTracer.setLevel( tracer.levels.INFO );
exampleTracer.debug( "This won't show ");
exampleTracer.info( "This will show");
// Do something that takes long
exampleTracer.startTimer();
for( var i = 0 ; i < 1000000 ; i++ ){
	// Do something that takes a while
}
exampleTracer.info( "How long did it take?");
exampleTracer.setLevel( tracer.levels.DEBUG );
exampleTracer.startTimer();
exampleTracer.resetMemory();
var webExample = Ti.UI.createWebView({
	url: "http://www.apple.com"
});
webExample.addEventListener('load', function(){
	exampleTracer.debug('web view load complete. How much memory did I use?');
});

Ti.UI.currentWindow.add( webExample );
exampleTracer.debug( "done");

I’ll also be adding a UI control that to can add to an app to set the logging level for each tracer dynamically.

Let me know what you think in the comments!

Date posted: September 12, 2011

0 Comments
Top
1 2 Page 1 of 2