You are on page 1of 8

Tutorial | Make a SQLite Mobile Database App for iPhone |

iOS

The iOS SDK has many data persistent solutions, including SQLite, Core Data, archiving, property
lists and files to name a few. Prior to Core Data, SQLite was the de facto standard for data
persistence on an iOS device. The database continues to be a favorite solution for developers over
Core Data. To help you learn how to implement SQLite within an iOS application I have written this
tutorial to demonstrates how to load and display data from a SQLite database in an UITableView and
to display selected details in a DetailViewController based on a user's selection. The tutorial also
shows how to insert data using an input form.
Create the Database
Before we can access the data in the database, it has to be created. SQLite can dynamically create
databases if it doesn't exists with the open command. This operation should be performed in the
AppDelegate when the app is loaded.
Open the AppDelegate implementation file and in the didFinishLaunchingWithOptions method
declare a variable for any possible error messages, NSError *errMsg.
Also add a BOOL variable which will be used to check to see if the database already exists in the
Document directory. If it doesn't exist, create it otherwise do nothing.
To get a handle on the Documents directory we will use the NSSearchPathForDirectoriesInDomains
method from the Foundations Framework. This method retrieves an array of all the directories in the
search path. For this example, the search path is the Document directory, NSDocumentDirectory,
which is a constant in the Foundations Framework.
Since the array will contain only one entry, the Documents directory, we can use the
ObjectAtIndex:0 to retrieve the first entry and store the value in a NSString variable,documentPath.
The NSDocumentDirectory returns the complete path to the Documents directory and so all we need
to do next is append the name of the database we will create and use to store the data.
The next line of code, fileExist = [[NSFileManager alloc] fileExistsAtPath:documentPath]; checks to
see if the database already exists at the path specified with the documentPath variable. If it does,
the return value will be YES. Otherwise the fileExistAtPath will return NO.
If the fileExist is false issue a open database command using the sqlite3_open SQLite command. If
the database fails to open (or get created) by checking the return code SQLITE_OK, log an error and
exit. Otherwise we will setup the query to create a table, todoTbl if it doesn't exist.
The sqlite3_exec command, used here, is a handy and compact command that encapsulates the
prepare statement, the step statement and the finalize statement which are the statements to
execute a SQLite query.
If the sqlite3_exec return code from the SQLITE_OK, we will have completed the database setup
process. The complete code is provided in code listing 1 below.

Code Listing 1: The didFinishLaunchingWithOptions Implementation


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
char *emsg;
BOOL fileExist;
//Get list of directories in Document path
NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
//Define new path for database
NSString * documentPath = [[dirPath objectAtIndex:0]
stringByAppendingPathComponent:@"taskDb.db"];
fileExist = [[NSFileManager alloc] fileExistsAtPath:documentPath];
if(fileExist)
if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK))
NSLog(@"An error has occured.");
else
const char *sqlTable = "create table if not exists todoTbl(todoName varchar, todoDescription
varchar, todoDate varchar)";
if(sqlite3_exec(db, sqlTable, NULL, NULL, &emsg) != SQLITE_OK)
NSLog(@"There is a problem with statement");
// Override point for customization after application launch.
return YES;
Switch to the implementation file and synthesize the todoArr NSMutableArray. Next we will
implement the selectTodo method because it will be the first method called when the app is loaded.
Any data in the database will read into the todoArr and displayed in the UITableView. This method
will load the data from the SQLite database using a SELECT query.
Next initialize the todoArr: todoArr = [[NSMutableArray alloc] init];
Since the database will be dynamically created using the sqlite3 library in the Documents directory,
we will need the path to this directory which will be returned as an array using the
NSSearchPathForDirectoriesInDomains Foundation function as in code listing 5

Code listing 7
if(sqlite3_prepare_v2(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
NSLog(@"There is a problem with prepare statement");
return;
else
while (sqlite3_step(sqlStatement)==SQLITE_ROW)
char * checkChar = (char*)sqlite3_column_text(sqlStatement, 1);
if (checkChar!=NULL)
ToDo * newToDo = [[ToDo alloc] init];
newToDo.todoName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement,
0)];
newToDo.todoDescription= [NSString stringWithUTF8String:(char
*)sqlite3_column_text(sqlStatement, 1)];
//get date value
const unsigned char *charDate = sqlite3_column_text(sqlStatement, 2);
if(charDate!=NULL)
//Convert char Date value to NSString
NSString *strDate = [[NSString alloc] initWithUTF8String: (const char *) charDate];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MM/dd/yyyy"];
newToDo.todoDate = strDate;
else
newToDo.todoDate = @"";
[todoArr addObject:newToDo];
newToDo = nil;
sqlite3_finalize(sqlStatement);
sqlite3_close(db);

Code listing 10 is the full implementation of the selectToDo method:


Code listing 10
-(void) selectToDo
int recordCount = 0;
todoArr = [[NSMutableArray alloc] init];
//Get list of directories in Document path
NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
//Define new path for database
NSString * documentPath = [[dirPath objectAtIndex:0]
stringByAppendingPathComponent:@"taskDb.db"];
if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK))
NSLog(@"An error has occured.");
return;
else
const char *sql = "select todoName, todoDescription, todoDate from todoTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare_v2(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
NSLog(@"There is a problem with prepare statement");
return;
else
while (sqlite3_step(sqlStatement)==SQLITE_ROW)
char * checkChar = (char*)sqlite3_column_text(sqlStatement, 1);
if (checkChar!=NULL)
ToDo * newToDo = [[ToDo alloc] init];
newToDo.todoName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement,
0)];

newToDo.todoDescription= [NSString stringWithUTF8String:(char


*)sqlite3_column_text(sqlStatement, 1)];
//get date value
const unsigned char *charDate = sqlite3_column_text(sqlStatement, 2);
if(charDate!=NULL)
//Convert char Date value to NSString
NSString *strDate = [[NSString alloc] initWithUTF8String: (const char *) charDate];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MM/dd/yyyy"];
newToDo.todoDate = strDate;
else
newToDo.todoDate = @"";
[todoArr addObject:newToDo];
newToDo = nil;
sqlite3_finalize(sqlStatement);
sqlite3_close(db);
The next implementation is the insert method upsertTodo. For this example the method only
performs inserts but should be modified to also provide updates to existing records as well. Also this
method assumes that the database already exist, which would need to be verified in a production
app.
The first to do is get the path of the Documents directory where the database is located and assign
the complete path to the documentPath variable as be did in the previous method, see listing 11.
Code listing 12
NSString *insertSQL = [NSString stringWithFormat:
@"INSERT INTO todoTbl(todoName, todoDescription, todoDate) VALUES ('%@','%@','%@')",
todoName,todoDescription, todoDate];
const char *sql = [insertSQL UTF8String];
sqlite3_stmt *sqlStatement;

Code listing 13
-(void) upsertToDo:(NSString *)todoName :(NSString *) todoDescription :(NSString *)todoDate
//Get list of directories in Document path
NSArray * dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
//Define new path for database
NSString * documentPath = [[dirPath objectAtIndex:0]
stringByAppendingPathComponent:@"taskDb.db"];
if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK))
NSLog(@"An error has occured.");
return;
else
NSString *insertSQL = [NSString stringWithFormat:
@"INSERT INTO todoTbl(todoName, todoDescription, todoDate) VALUES ('%@','%@','%@')",
todoName,todoDescription, todoDate];
const char *sql = [insertSQL UTF8String];
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare_v2(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
NSLog(@"Problem with prepare statement");
return;
else
if(sqlite3_step(sqlStatement)==SQLITE_DONE)
sqlite3_finalize(sqlStatement);
sqlite3_close(db);
Code listing 16
#import <UIKit/UIKit.h>
#import "DbOps.h"

#import "ToDo.h"
#import "klViewController.h"
#import "todoDetailViewController.h"
@interface ListViewController : UITableViewController<UITableViewDataSource,
UITableViewDelegate>
@property(nonatomic, strong) ToDo * todoItem;
@property(nonatomic, strong) DbOps * ops;
@property(nonatomic, strong) NSMutableArray * todoArray;
@property (strong, nonatomic) IBOutlet UITableView *todoTable;
@end
With the UITableViewController selected, create a navigation controller using the (Editor->Embed
In->Navigation Controller) command from the Editor menu in Xcode.
Next add a Navigation Item button to the Navigation Bar in the UITableViewController and add a
Push Segue to the klViewController that was initially created with the project. Read this tutorial"
IOS 5 Storyboarding Tutorial using Segues | Scenes | View Controllers | Navigation", if you need
help creating Scenes and Segues.
The complete layout of the Storyboard is presented in Figure 1 below
Implement the View Controller
Now that the Storyboard is done, we will add code to the implementation files.
Open the klViewController implementation, or whatever you call it the initial view controller when
you created the project
In the saveToDo method add the following code, listing 17, to pass the values of the IBOutlets
(UITextFields) to the upsertToDo method:
That is all that is needed for the klviewController implementation
In the ListViewController, in the viewWillAppear method, add the following code, listing 18, to
initialize the todoArray, initialize the ops instance variable, call the selectTodo method and assign
the values from the todoArr to the todoArray variable. This will load any existing items into the
UITableView when it is loaded.
To be able to reload any new data at runtime, call the reloadData method as in coding listing 18.
In Summary
Below are screenshots of the running app. Of course this is a rudimentary design and its objective is

to provide an example of how to display content from a SQLite database in an UITableView and also
how to push the details to a second view controller. However in a real production app, more error
checking and proper controls and validation must be implemented, not to mention proper testing to
make sure the application performs as desired. The code itself is only for demonstration purposes
only and the author doesn't provide any guarantees on its functionality in any application where it is
implemented.
http://klanguedoc.hubpages.com/hub/iOS-5-How-To-Display-SQLite-Data-in-a-UITableView-and-Detai
lViewController

You might also like