Most of the time, your iPhone application data storage needs will be taken care of by simple object serialization and flat file storage. However, there comes a point when that type of storage just won't work anymore - when you start storing thousands or tens of thousands of objects. Apple recognized that fact, and so gave app developers the ability to use SQLite inside their applications.
What is SQLite? Well SQLlite is a small but powerful database engine that takes virtually no configuration to set up and lives in a single file. It is extremely handy for quick and easy databases. We have actually talked about SQLite here before at Switch On The Code a couple of times. For a thorough understanding of how to interact with SQLite, I would suggest reading through the Writing a .NET Wrapper for SQLite tutorial - because we won't be going too deep in to SQLite today in this tutorial.
Ok, so first things first - we need to get an iPhone Xcode project set up to use SQLite. This isn't terribly hard - we just need to add a library to our project. So right click on the Framworks folder in Xcode and choose "Add Existing File". This is because while what we are adding is a library, it isn't a "Framework" in the standard frameworks folder. You will want to navigate to the following convoluted path: /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSilumator3.0.sdk/usr/lib and choose the file libsqlite3.dylib:
Ok, so I lied a little bit about the path above. You actually will want to modify that path a bit depending on the current SDK you have Xcode set to use (for instance, mine currently is "Simulator - 3.0"). This is because of an option we are about to set in the second Add dialog - the "Reference Type":
By setting the "Reference Type" as "Relative to Current SDK", this makes sure that Xcode will always use the correct libsqlite3.dylib file for whichever SDK we are using. We also don't want to "copy items into destination group's folder" - we want the library to stay right where it is - so uncheck that if it is checked.
Ok, now it is time to make some use of this library in code. I'm going to be making a very silly application here today that records every time you start up the application, and displays all the start times in alert dialogs. Extremely useless, but it will show off inserting into and selecting from SQLite tables. We have a single view application with nothing on the view, and all it does is display alerts:
Code time!
// SOTC_SQLiteExampleViewController.m
// SOTC-SQLiteExample
#import "SOTC_SQLiteExampleViewController.h"
#import "/usr/include/sqlite3.h"
@implementation SOTC_SQLiteExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIAlertView *view;
sqlite3 *database;
int result = sqlite3_open("/myExampleDatabase.db", &database);
if(result != SQLITE_OK)
{
sqlite3_close(database);
view = [[UIAlertView alloc]
initWithTitle: @"Database Error"
message: @"Failed to open database."
delegate: self
cancelButtonTitle: @"Hrm." otherButtonTitles: nil];
[view show]
[view autorelease];
return;
}
}
@end
First off, we need to include a header file to be able to use any of the SQLite functions - /usr/include/sqlite3.h. All this gives us is a pure C function interface to SQLite - no classes, no ORM layer, no nothing. Just hardcore database calls. This makes for some ugly code, but hey, it is what we have to work with.
Now that we have those calls, we can open/create a database. That is what the sqlite_open call does. If there is a file at that path, it tries to open it as a SQLite3 database file - and if not, a new file is created. If stuff goes wrong, an error code will be returned, which we can check. If everything went well, the return code will be SQLITE_OK.
Technically, you should check the return value of every SQLite call for errors. I'm going to be lax and not do it today - but that doesn't mean you should!
"CREATE TABLE IF NOT EXISTS Messages (ID INTEGER PRIMARY KEY AUTOINCREMENT, Message TEXT)",
NULL, NULL, NULL);
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"EEEE MMMM d, YYYY h:mm a, zzz"];
sqlite3_exec(database, [[NSString stringWithFormat:
@"INSERT INTO Messages VALUES(NULL, 'You ran the app at %@')",
[dateFormat stringFromDate:today]]
UTF8String],
NULL, NULL, NULL);
[dateFormat release];
sqlite3_stmt *statement;
sqlite3_prepare_v2(database, "SELECT Message FROM Messages", -1, &statement, nil);
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *message = [[NSString alloc] initWithUTF8String:
(char *)sqlite3_column_text(statement, 0)];
view = [[UIAlertView alloc]
initWithTitle: @"A Message"
message: message
delegate: self
cancelButtonTitle: @"Woot!" otherButtonTitles: nil];
[view show];
[view autorelease];
[message release];
}
sqlite3_finalize(statement);
sqlite3_close(database);
Because this database may or may not have exited before we opened the file, the first thing we need to do is ensure that our tables exist before we do anything. We do this using the sqlite3_exec call - which is used for sql statements that don't return a result set. Once we have a table, we can do an insert of today's date and time - using some NSDateFormatter and stringwithFormat action.
One thing to note (and is very important) - the SQLite function calls expect C-Strings - not NSStrings. This is why for most of these function calls, you don't see the "@" sign at the start of the string. For this insert call, we needed to use an NSString to take advantage of the date formatter - but we have to get to a C-String before calling the sqlite function. This is done through the UTF8String call on a NSString - it returns a good ol' char array that sqlite can deal with.
Next we have the select statement. Since these are pure C calls, we don't get any of the niceties like a "result set object" or anything like that. We "prepare" a statement using the sqlite3_prepare_v2 call and then iterate through the results using the sqllite3_step call. And just like with that insert statement, we have to convert the string data returned (although this time from a C-String into an NSString). This is just as easy - we just call the initWithUTF8String, and we get a populated NSString object. Oh, and when you are done with a select, don't forget to sqlite3_finalize the statement.
Then we close the database, and that is it for this whirlwind tour of SQLite on the iPhone! You can grab the project for this silly example below, and if you have any questions, leave them in the comments or in the forums.
09/17/2009 - 23:25
Hi,
Thanks for this wonderful tutorial. It helped me a lot.
I still have one problem. It goes likes this.
I am having 2 text box and 2 buttons. After typing some text in both the text boxes and click a button say SUBMIT, the values that are present in both the text boxes has to be saved in the SQLite. When I click on the another button named DISPLAY, the record in the SQLite table ( Assume there is only one table and only one record for that application.) should be displayed in the 2 text boxes.
The record is not getting saved in SQLite and so its not getting displayed again.
Could you please help me with the above problem. I will be grateful to you :-)
Thanks & Regards
Ram
11/09/2009 - 23:48
Hi Ram
The database is read only, copy the database to the documents folder before saving any data
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager release];
}
[objc]
Thanks
11/25/2009 - 06:43
HI Ram,
I am new to iPhone development.And now my task is what you have done.If you have completed then please upload the source code.It will be very helpful for me.
Thanks in advance
07/13/2010 - 04:56
how do i delete data from the data base?
can any one help me plz!!!!!!!!!
07/13/2010 - 07:55
It's done very similar to how data was inserted in the posted example, except use a DELETE statement instead.
WHERE some_column=some_value
So to delete the row created in the example, it would be:
WHERE Message="You ran the app at Tuesday August 25, 2009 12:14 PM, EDT"
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.