June 30, 2008

QuickConnectiPhone framework beta 2

Posted in iPhone development tagged , , , , , , , , , , , , , at 10:51 pm by tetontech

A new version of QuickConnectiPhone is now available. It includes the ability to push Core Location and Acceleration information up into the JavaScript running in your UIWebView. It is open-source and free. You can get it from sourceForge. If you checkout my Blogroll to the lower right of this page you will find a link to the site. Or you could go directly here.

As with Beta 1 it allows you to write your application in Dashcode using HTML, JavaScript, and CSS and then install it and run it on your device. You application will then run without any network connections of any type.

It also lets you store persistent data in SQLite using the new JavaScript API to SQLite. An easy to use wrapper is provided for you within the framework. If you do choose to get data from a web server the framework provides you with an easy to use wrapper for AJAX calls that has the same API as the SQLite wrapper.

Since you are writting your application to run inside of the UIWebView can also use the new CSS transitions, animations, etc. it makes available.

Give it a try and give me some feedback.

June 28, 2008

iPhone Objective-C SQLite development

Posted in iPhone development tagged , , , , , , , , , , , , , , at 5:46 pm by tetontech

The code included here is now available in QuickConnectiPhone 1.1.3 and can be downloaded from the sourceforge repository. A 1.5 Beta 8 version is also available.  The Beta 8 version is much more flexible and stable than the simple code you see here.

I have been working on the Objective-C version of QuickConnect.  Included in it is a simple wrapper for interacting with the SQLite database.  QuickConnectOC will, when I have finished testing it, work for both the iPhone and for Mac applications.  Of course on a Mac you could use the CoreData Framework but this is not available, from what I can tell, as a framework for the iPhone.  

This SQLiteDataAccess class depends on Shawn Ericksons’ good work in creating an easy to use class for creating singletons in Objective-C, FTSWAbstractSingleton.

Since SQLiteDataAccess is a singleton, you can call it from anywhere in your code without passing pointers to it all over the place.  It is also thread safe for those who are creating multi-threaded apps.

Here is an example of me using it to retrieve some information from a database for an iPhone eBook application framework I am writing.

I have just refreshed the code on this page to include some bug fixes.

SQLiteDataAccess *theDatabase = [SQLiteDataAccess getInstance:@"data.sqlite" isWriteable:FALSE];
NSString* SQL = @"Select * from book_info";
DataAccessResult *aDAResult = [theDatabase getData:SQL withParameters:nil];
/*
* Retrieve the data from the DAResult and return it.
* You do not want to return the DAResult since that would tightly couple
* the database implementation to many other areas of the application.
*
* If you are going to use the error checking or the other information
* found in the DAResult object you need to use it here.
*/
//we are only interested in the first row. That is all there should be anyway.
NSArray *data = [[aDAResult results] objectAtIndex:0];
[aDAResult release];

As you can see it is simple to use.  When you want to retrieve information you use the ‘getData’ method and when you want to modify the database in any way you use the ‘setData’ method.  This is the same as the other QuickConnect DataAccess classes in the various languages and for various databases.  It is also the same API as when using the ServerAccessObject JavaScript class, an AJAX wrapper, in the QuickConnectiPhone framework.  

I am posting here the incomplete and only partially tested source code so that you can see how to use prepared statements, etc. with SQLite and Objective-C.  This code has only been tested for simple SQL statements that are not prepared statements or transactions at this time.  I expect that the current code is close to being ‘right’ but can’t guarantee it.  I expect to soon have it tested to a degree that I am more comfortable with.

When I am more confident in the code I will post it as part of QuickConnectOC on the QuickConnect sourceForge web site.

When complete it will also include such items as helper functions that return the auto-generated id for calls that INSERT INTO tables with auto-incrementing id fields, etc.

Header File

 
// SQLiteDataAccess.h

/*
Copyright 2008 Lee S. Barney

This file is part of QuickConnectOC.

QuickConnectOC is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

QuickConnectOC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with QuickConnectOC. If not, see .

*/

#import
#import
#import "DataAccessResult.h"
#import "FTSWAbstractSingleton.h"

@interface SQLiteDataAccess : FTSWAbstractSingleton {
// Opaque reference to the SQLite database.
sqlite3 *database;
// Opaque reference to the dictionary that maps strings to binding function names
NSDictionary *bindTypeDictionary;
}

- (DataAccessResult*)getData:(NSString*)SQL withParameters:(NSArray*)parameters;
- (DataAccessResult*)setData:(NSString*)SQL withParameters:(NSArray*)parameters;
- (DataAccessResult*)startTransaction;
- (void)endTransaction;
- (void)rollback;
- (void)close;

+ (SQLiteDataAccess*)getInstance: (NSString*) dbName isWriteable: (BOOL) isWriteable;

 

 

 

 

@end
Implementation file
// SQLiteDataAccess.m

/*
Copyright 2008 Lee S. Barney

This file is part of QuickConnectOC.

QuickConnectOC is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

QuickConnectOC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with QuickConnectOC. If not, see .

*/

#import "SQLiteDataAccess.h"
#import "QCParameter.h"

// Private interface for AppDelegate - internal only methods.
@interface SQLiteDataAccess (Private)
- (DataAccessResult*)dbAccess:(NSString*)SQL withParameters:(NSArray*)parameters treatAsChangeData:(BOOL)treatAsChangeData;
//internal bind methods
- (int) bind_blob:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_double:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_int:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_text:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_zeroblob:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_null:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
/*
* private initialization method
*/
- (SQLiteDataAccess*)initWithDatabase: (NSString*) dbName isWriteable: (BOOL) isWriteable;

@end

// SQLiteDataAccess.m

/*
Copyright 2008 Lee S. Barney

This file is part of QuickConnectOC.

QuickConnectAJAX is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

QuickConnectAJAX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with QuickConnectAJAX. If not, see .

*/

#import "SQLiteDataAccess.h"
#import "QCParameter.h"

// Private interface for AppDelegate - internal only methods.
@interface SQLiteDataAccess (Private)
- (DataAccessResult*)dbAccess:(NSString*)SQL withParameters:(NSArray*)parameters treatAsChangeData:(BOOL)treatAsChangeData;
//internal bind methods
- (int) bind_blob:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_double:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_int:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_text:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_zeroblob:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
- (int) bind_null:(sqlite3_stmt*)statement withIndex:(int) withBindVariable:(id)aVariable;
/*
* private initialization method
*/
- (SQLiteDataAccess*)initWithDatabase: (NSString*) dbName isWriteable: (BOOL) isWriteable;

@end

@implementation SQLiteDataAccess

/*
* static method to implement singleton pattern
*/
+ (SQLiteDataAccess*)getInstance: (NSString*) dbName isWriteable:(BOOL)isWriteable{
/*SQLiteDataAccess* mySelf = [self singleton];
return [mySelf initWithDatabase:dbName isWriteable:isWriteable];
*/
//since this line is declared static it will only be executed once.
static SQLiteDataAccess *mySelfSQLiteDA = nil;

@synchronized([SQLiteDataAccess class]) {
if (mySelfSQLiteDA == nil) {
mySelfSQLiteDA = [SQLiteDataAccess singleton];
mySelfSQLiteDA = [mySelfSQLiteDA initWithDatabase:dbName isWriteable:isWriteable];
}
}
return mySelfSQLiteDA;
}
- (SQLiteDataAccess*)initWithDatabase: (NSString*) dbName isWriteable: (BOOL) isWriteable{
//if (self = [super init]) {
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"data.sqlite"];
if(isWriteable){

// The application ships with a default database in its bundle. If anything in the application
// bundle is altered, the code sign will fail. We want the database to be editable by users,
// so we need to create a copy of it in the application's Documents directory.

BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"data.sqlite"];
NSLog(writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (!success){
// The writable database does not exist, so copy the default to the appropriate location.
success = [fileManager copyItemAtPath:path toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, @"Failed to create writable database file with message '%@'.", [error localizedDescription]);
return nil;
}
}
path = writableDBPath;
}
sqlite3 *aDatabase;
NSLog(@"path: %@",path);
if (sqlite3_open([path UTF8String], &aDatabase) == SQLITE_OK) {
NSLog(@"database opened");
self->database = aDatabase;

NSLog(@"assigned");
//create the dictionary that maps parameter types to bind method calls
NSArray *keys = [NSArray arrayWithObjects:@"b", @"d", @"i", @"i63", @"t", @"t16", @"z", @"nil",nil];
NSLog(@"keys");
NSArray *values = [NSArray arrayWithObjects:@"bind_blob:withIndex:withBindVariable", @"bind_double:withIndex:withBindVariable",
@"bind_int:withIndex:withBindVariable", @"bind_int64:withIndex:withBindVariable",
@"bind_text:withIndex:withBindVariable", @"bind_text16:withIndex:withBindVariable",
@"bind_zeroblob:withIndex:withBindVariable", @"bind_null:withIndex:withBindVariable", nil];

NSLog(@"keys and objects ready");
NSDictionary *aDictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];

NSLog(@"dictionary ready");
self->bindTypeDictionary = aDictionary;
NSLog(@"dictionary set");
NSLog(@"successfully loaded database");
return self;
}
else{
//since we failed to open the database completely close it down to make sure that everyting is cleaned up
sqlite3_close(aDatabase);
NSAssert1(0, @"Failed to open database with message '%s'.", sqlite3_errmsg(aDatabase));
}
//}
return nil;
}

- (DataAccessResult*)getData:(NSString*)SQL withParameters:(NSArray*)parameters{
NSLog(@"getting data");
return [self dbAccess:SQL withParameters:parameters treatAsChangeData:FALSE];
}

- (DataAccessResult*)setData:(NSString*)SQL withParameters:(NSArray*)parameters{
return [self dbAccess:SQL withParameters:parameters treatAsChangeData:TRUE];
}

- (DataAccessResult*)dbAccess:(NSString*)SQL withParameters:(NSArray*)parameters treatAsChangeData:(BOOL)treatAsChangeData{
NSLog(@"in dbAccess");
DataAccessResult *theResult;
theResult = [DataAccessResult alloc];
if(parameters != nil && [parameters count] > 0){
//make sure the the number of parameters is equal to the number of qestion marks in the SQL string
}
NSMutableArray* results = [[NSMutableArray alloc] initWithCapacity:0];
int numResultColumns = 0;
sqlite3_stmt *statement = nil; // Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
const char* SQLChar = [SQL UTF8String];
NSLog(@"about to prepare %@",SQL);
if (sqlite3_prepare_v2(database, SQLChar, -1, &statement, NULL) == SQLITE_OK) {
if(!treatAsChangeData){
NSLog(@"columns not changing");
//retrieve the number of columns in the result of the execution of the select statement
numResultColumns = sqlite3_column_count(statement);
NSLog(@"numRecentColumns: %i",numResultColumns);
NSMutableArray *fieldNames = [[NSMutableArray alloc] initWithCapacity:0];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < numResultColumns; i++){
const char *name = sqlite3_column_name(statement, i);
NSString * columnName = [[NSString alloc]initWithCString:name encoding:NSUTF8StringEncoding];
[fieldNames addObject:columnName];
}
[theResult setFieldNames:fieldNames];

[pool release];
}
if(parameters != nil){
int numParams = [parameters count];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < numParams; i++) {
QCParameter *parameter = [parameters objectAtIndex:i];
NSString *name = [parameter name];
id value = [parameter value];
NSString *funcType = [bindTypeDictionary objectForKey:name];
SEL aSelector = NSSelectorFromString(funcType);
//bind the variables here
objc_msgSend(self, aSelector, statement, i, value);

}
[pool release];
}
NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:0];

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// We "step" through the results - once for each row.
// if the statement executed is not a select statement sqlite3_step will return SQLITE_DONE on the first iteration.
while (sqlite3_step(statement) == SQLITE_ROW) {
if([theResult columnTypes] == nil){
NSMutableArray *columnTypes = [[NSMutableArray alloc] initWithCapacity:0];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < numResultColumns; i++){
NSNumber * columnType = [NSNumber numberWithInt:sqlite3_column_type(statement,i)];
[columnTypes addObject:columnType];
}
[theResult setColumnTypes:columnTypes];
[pool release];
}
NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:numResultColumns];
/*
* Iterate over all of the columns. Determine their type and retrieve its value
* SQLITE_INTEGER
* SQLITE_FLOAT
* SQLITE_BLOB
* SQLITE_NULL
* SQLITE_TEXT
*/

for(int i = 0; i < numResultColumns; i++){
int type = [[[theResult columnTypes] objectAtIndex:i] intValue];
if(type == SQLITE_INTEGER){
NSLog(@"integer: %i",sqlite3_column_int(statement, i));
NSNumber *aNum = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, i)];
[row addObject:aNum];
}
else if(type == SQLITE_FLOAT){
NSLog(@"float");
NSNumber *aFloat = [[NSNumber alloc] initWithFloat:sqlite3_column_double(statement, i)];
[row addObject:aFloat];
}
else if(type == SQLITE_TEXT){
NSLog(@"text");
NSString *aText = [[NSString alloc]initWithCString:sqlite3_column_text(statement, i) encoding:NSASCIIStringEncoding];
[row addObject:aText];
}
else if(type == SQLITE_BLOB){
NSLog(@"blob");
NSData *aData = [[NSData alloc]dataWithBytes:sqlite3_column_blob(statement, i) length:sqlite3_column_bytes(statement,i)];
[row addObject:aData];

}
else{//if([[columnTypes objectAtIndex:i] intValue] == SQLITE_NULL){
[row addObject:@"null"];
}
}
[results addObject:row];
}
[pool release];
[theResult setResults:results];
}
else{
NSString *error;
error = [[NSString alloc]initWithCString:sqlite3_errmsg(database) encoding:NSASCIIStringEncoding];
[theResult setErrorDescription:error];
[error release];
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);

return theResult;
}

- (DataAccessResult*)startTransaction{
NSString* sql = @"BEGIN EXCLUSIVE TRANSACTION";
return [self setData:sql withParameters:nil];
}
- (void)endTransaction{
NSString* sql = @"COMMIT";
[self setData:sql withParameters:nil];

}
- (void)rollback{
NSString* sql = @"ROLLBACK";
[self setData:sql withParameters:nil];
}

- (void)close{
if (sqlite3_close(database) != SQLITE_OK) {
NSAssert1(0, @"Error: failed to close database with message '%s'.", sqlite3_errmsg(database));
}
}

// internal bind methods

- (int) bind_blob:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(NSData*)aVariable{

if (![aVariable respondsToSelector:@selector(lengthOfBytes:)]) {
return -1;
}
//by default have the library make a copy, SQLITE_TRANSIENT, since we don't know if the variable may be changed
//by something else in the application.
return sqlite3_bind_blob(statement, parameterIndex, aVariable, [aVariable length], SQLITE_TRANSIENT);
}
- (int) bind_double:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(double)aVariable{
return sqlite3_bind_double(statement, parameterIndex, aVariable);
}
- (int) bind_int:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(int)aVariable{
return sqlite3_bind_int(statement, parameterIndex, aVariable);
}
- (int) bind_text:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(id)aVariable{
//assume an ASCII string
return sqlite3_bind_blob(statement, parameterIndex, aVariable, [aVariable lengthOfBytesUsingEncoding:NSASCIIStringEncoding], SQLITE_TRANSIENT);

}
- (int) bind_zeroblob:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(int)aVariable{
return sqlite3_bind_zeroblob(statement, parameterIndex, aVariable);
}
- (int) bind_null:(sqlite3_stmt*)statement withIndex:(int)parameterIndex withBindVariable:(id)aVariable{
return sqlite3_bind_null(statement, parameterIndex);
}

@end

June 18, 2008

UIWebView the only class for complex text display on the iPhone

Posted in iPhone development tagged , , , , , at 3:58 am by tetontech

The other day I was exploring the iPhone API looking for a view that would allow me to display text in a complex form like is accomplishable within the UIWebView.  As I did so, I found the UITextView and initially thought I had found the Objective-C analog to what we can do in the UIWebView.  As I read the description in the API documentation I came across this statement.

This class does not support multiple styles for text. The font, color, and text alignment attributes you specify always apply to the entire contents of the text view. To display more complex styling in your application, you need to use a UIWebView object and render your content using HTML.

Now I know one of the reasons that there has been so much interest in how to embed and use UIWebView.  Of course the other is the ability to draw data from multiple web sources easily.
I have enjoyed the ease with which I have been able to accomplish data display using the embedded webkit and have been pleased with its’ speed on the phone considering the limited CPU of a mobile device.
I am now playing with how to get both vertical scroll and horizontal swipe to work in a hybrid application.  The issue is that the UIWebView captures the swipe as a horizontal scroll.  The approach I am taking is to use a transparent view that overlays the UIWebView and then push all of the gesture events either into the UIWebView or into the JavaScript similar to the acceleration events in the example in a previous post.
I’ll let you know the results when I get it working.

June 11, 2008

Hybrid Mac applications

Posted in misc tagged , , , , , , , , , , at 10:45 pm by tetontech

The code example here is now included in a framework called QuickConnectiPhone.  If you want to know more have a look at this post as well as others in this blog.

In a previous post I showed how a UIWebview can be programmatically inserted into an application running on the iPhone using the new SDK.  In this post I will explain how to insert a WebView into a Cocoa application running on the Mac.  This example will show how to use the new Interface Builder that ships with the new SDK.  I will also cover this soon for the iPhone.

There are a series of step that you should go through to accomplish this task.  The names for items created in this example should be replaced with what you want them to be.

Steps:

  1. Select ‘New Project’ in the file menuThe project type selection window
  2. Select the ‘Cocoa Appliction’ project template
  3. Name the new project.  Mine will be called ‘example3’entering the name of the new project
  4. Create and name a new Objective-C file.  I will call mine AppDelegatethe Cocoa file creation window
  5. Open AppDelegate.h and add ‘IBOutlet id webView;’ to the class.  This is the name that will be associated with the WebView that we will later drag and drop when we are using Interface Builderadding an outlet to the header
  6. Add the WebKit Framework to the project.  On my machine this is located in
    ‘/Developer/SDKs/MacOS10.5.sdk/System/Library/Frameworksthe dialog used to select the WebKit framework
  7. Open the MainMenu.xib file by double clicking it.  This will open Interface Builder.  The xib file is found in the Resources group in your project.
  8. Open the library window by selecting it in the ‘Tools’ menu.
  9. Drag the ‘Object’ cube from the library to the ‘MainMenu.xib’ windowa screen shot showing an Objective-C object being added to the xib file
  10. Select ‘identity’, the ‘i’ tab in the inspector window labeled ‘Object identity’ and change the Class to be ‘AppDelegate’ or what ever you named the Objective-C class you created in step 4.  This will create a link between the xib file and the Objective-C class.  Notice that as soon as you rename the class the id you created in the header file in step 5 is now listed for the object in Interface builder as an outlet.  This is what will allow us to gain access to the WebView we will soon create in Interface Builder.rename the object to link it to the Objective-C files created earlier.
  11. Add a WebView to the ‘Window’ window by selecting ‘Web Kit’ from the ‘Objects’ tab of the library window and then dragging the WebView element to the ‘Window’ window.  A WebView will now appear in the ‘Window’ window that you can resize and locate anywhere in the window you would like.  I will make mine slightly smaller than the containing window.  You will also want to select the ruler tab in the ‘inspector’ window.  By clicking on the light red arrows within the box in the ‘Autosizing’ section your WebView will stretch as the window stretches.placing the WebView element in the main app window
  12. Select the App Delegate item in the MainMenu.xib window and display the connections tab, select the ‘->’ in the blue circle. A small circle will exist to the right of the outlet ‘webView’.  Drag it onto the WebView in the ‘Window’ window.  This will cause an outlet connection to be made.  This means that in the code your Xcode Objective-C code the ‘webView’ you added to the header file in step 5 will in essence be a pointer/reference to the WebView you created in Interface Builder in step 11.linking the WebView to the outlet
  13. In the ‘Referencing Outlet’ section of this same inspector window for the App Delegate there is a line that says ‘New Referencing Outlet’.  Select the small circle on the right and drag it over ‘Files Owner’ in the ‘MainMenu.xib’ window.  A popup will be shown that says ‘delegate’.  Select it.  You class ‘App Delegate’ will now be used as the delegate for the application.  This will allow you to now create functions that are triggered for events that happen in the application.  Step 14 will be the implementation of one of these.linking the AppDelegate class to be the delegate for the application
  14. Save the you changes by saving within Interface Builder.
  15. Implement the applictionDidFinishLoading method in AppDelegate.m.  This is where you can write out to the log file using NSLog and tell the WebView to load a specific web page as well as other actions you may want to have happen when the application has completed loading.  In this example we will load a page found locally on the machine that is part of the application itself.  It is called ‘index.html’ and can be found in the ‘Resources’ group in the Xcode project.  The code in the image shows you how to load this web page.implementing the application Did Finish Launching method
  16. Compile and select the ‘Build and Go’ icon to run the application.the application running

June 4, 2008

WebKit speeds

Posted in iPhone development, misc, using an iPhone tagged , , , , , , , , at 4:49 am by tetontech

A year ago I created a test to see how different JavaScript loop types compared based on speed. I compared a variant for loop against an invariant for loop and the for each loop. The test results, which can be found in the book Oracle Database Ajax & PHP Web Application Development (Oracle Press) by myself and Michael McLaughlin, showed that for all of the browsers examined the for each loop was much slower than the already slow variant loop and that the invariant loop was significantly faster.
If we were iterating over the elements in an array or map a standard looking variant loop would look like

for(var i = 0; i < array.length; i++)

An invariant loop would be

var len = array.length;
for(var i = 0; i < len; i++)

and a for each loop would look like

for (var i in array)

While the original tests were looking for percent speed improvements within a browser achievable by selecting the different types of loops I thought it would be interesting to modify the test to report raw speeds in milliseconds and then compare various browsers.
The speeds reported here are for the fast invariant for loop over a series of runs.
Safari 3.0 – 98 milliseconds
WebKit 3.1.1 – 37 milliseconds
Firefox 2.0.0.14 – 200 milliseconds
Firefox 3.0 RC1 – 28 milliseconds

The new WebKit shows a dramatic increase over the older Safari as does the new Firefox. WebKit appears to be nearly 3 times faster in this test than its’ older version used in Safari. This may be due to the changing of the JavaScript interpreter to SquirrelFish in the place of the older one. A discussion of SquirrelFish and why it is faster can be found at WebKit’sblog.
It will be interesting to see if this new version makes it into the upcoming iPhone OS 2.0. It would be fantastic if it was stable enough and did. Every bit of speed you can get on a handheld device is precious.

%d bloggers like this: