I've written a simple app that uses Sqlite database. It works great on iPhone simulator but doesn't work on my iPhone.
-(NSString *) getFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,YES);
NSString *documentsDir=[paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"database.sql"];
}
-(void)openDatabase
{
//Open
if (sqlite3_open([[self getFilePath] UTF8String], &db) != SQLITE_OK ) {
sqlite3_close(db);
NSAssert(0, #"Database failed to open.");
}
}
Output on Xcode after launching app:
2013-03-07 02:12:16.525 SqliteWorkApp[464:907] *** Assertion failure in -[SqliteWorkAppViewController insertRecord], /Users/cmltkt/Objective-C Apps/SqliteWorkApp/SqliteWorkApp/SqliteWorkAppViewController.m:77
2013-03-07 02:12:16.529 SqliteWorkApp[464:907] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error updating table.'
*** First throw call stack:
(0x3398e2a3 0x3b82797f 0x3398e15d 0x34263ab7 0x19b3b 0x195bd 0x357b5595 0x357f5d79 0x357f1aed 0x358331e9 0x357f683f 0x357ee84b 0x35796c39 0x357966cd 0x3579611b 0x374885a3 0x374881d3 0x33963173 0x33963117 0x33961f99 0x338d4ebd 0x338d4d49 0x357ed485 0x357ea301 0x19147 0x3bc5eb20)
libc++abi.dylib: terminate called throwing an exception
insertRecord function:
-(void)insertRecord
{
NSString *sql = [NSString stringWithFormat:#"INSERT OR REPLACE INTO 'countries' ('name', 'flag') " "VALUES ('Sample Data','Sample Data')"];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err)
!= SQLITE_OK) {
sqlite3_close(db);
NSAssert(0, #"Error updating table.");
}
}
I had same problem,
I used SQLite Db portable file in app and It was working on simulator very well but not on real device.
So after digging a lot, I found, when I dragged sqlite db file into my project, Xcode did not add it to bundle resources.
Please! go this way
select your project go to "Build Phases"
add your database.sqlite (it should be. sqlite as i know) file to Bundle resources.
and For handling all SQLite stuff, my database helper class code is
#import "ASCODBHelper.h"
#import <sqlite3.h>
#implementation ASCODBHelper
static ASCODBHelper *db;
+(ASCODBHelper *)database{
if (db == nil) {
db = [[ASCODBHelper alloc] init];
}
return db;
}
- (id)init{
self = [super init];
if (self) {
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"IOSMeeting" ofType:#"sqlite"];
if (sqlite3_open([sqLiteDb UTF8String], &db) != SQLITE_OK) {
NSLog(#"Failed to open database!");
}
}
return self;
}
-(void)getPresentationDeatilById:(NSString *)presentationid andSessionId:(NSString *)sessionid{
NSInteger pId = [presentationid integerValue];
NSInteger sId = [sessionid integerValue];
NSString *queryString = [[NSString alloc] initWithFormat:#"SELECT distinct mediaID,mediaURL,meetingName,trackName FROM Media WHERE presentationID='%d' and sessionID ='%d'",pId,sId];
NSLog(#"query is : %#",queryString);
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(db, [queryString UTF8String], -1, &selectStatement, nil) == SQLITE_OK) {
while (sqlite3_step(selectStatement) == SQLITE_ROW) {
char *mediaid = (char *) sqlite3_column_text(selectStatement, 0);
char *mediaurl = (char *) sqlite3_column_text(selectStatement, 1);
char *meetingname = (char *) sqlite3_column_text(selectStatement, 2);
char *topicname = (char *) sqlite3_column_text(selectStatement, 3);
NSString *mediaID = [[NSString alloc] initWithUTF8String:mediaid];
NSString *mediaURL = [[NSString alloc] initWithUTF8String:mediaurl];
NSString *topicName = [[NSString alloc] initWithUTF8String:topicname];
NSString *meetingName = [[NSString alloc] initWithUTF8String:meetingname];
//you can log here results
}
sqlite3_finalize(selectStatement);
}
}
#end
and Here is the code how I am using this.
Just import
#import "ASCODBHelper.h"
and call our db helper method this way
[[ASCODBHelper database] getPresentationDeatilById:presentationId andSessionId:sessionId];
Let me know if need help in this.
Related
Using MacOS Terminal I created a database name database.sql and inserted some records. Using iOS I can retrive the inserted values.
But using iOS code I tried to insert the record to the database and it does not enter the record in the database.
Should I set the need to set some permission? This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self openDB];
}
-(IBAction)save:(id)sender{
[self insertRecordIntoTableNamed:#"Contacts"
field1Value:fname.text
field2Value:lname.text
field3Value:comp.text
field4Value:email.text
field5Value:pnumber.text
field6Value:mnumber.text
field7Value:add.text
field8Value:city.text
field9Value:state.text];
}
-(void)openDB{
NSString *sqlfile=[[NSBundle mainBundle]pathForResource:#"database" ofType:#"sql"];
if(sqlite3_open([sqlfile UTF8String], &db)!= SQLITE_OK){
sqlite3_close(db);
NSLog(#"Database connected");
NSAssert(0,#"Database failed to open");
}
else
{
NSLog(#"Database connected");
}
}
-(void) insertRecordIntoTableNamed:(NSString *) tableName
field1Value:(NSString *) field1Value
field2Value:(NSString *) field2Value
field3Value:(NSString *) field3Value
field4Value:(NSString *) field4Value
field5Value:(NSString *) field5Value
field6Value:(NSString *) field6Value
field7Value:(NSString *) field7Value
field8Value:(NSString *) field8Value
field9Value:(NSString *) field9Value {
NSString *sql = [NSString stringWithFormat:#"INSERT INTO %# VALUES ('%#','%#','%#','%#',%#,%#,'%#','%#','%#');",tableName, field1Value, field2Value,field3Value,field4Value,field5Value,field6Value,field7Value ,field8Value,field9Value];
NSLog(#"%#",sql);
// char *err;
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil)== SQLITE_OK)
{
if (SQLITE_DONE!=sqlite3_step(statement))
{
sqlite3_close(db);
NSAssert(0, #"Error updating table.");
}
else{
NSLog(#"Success");
NSLog(#"%#",sql);
}
}
sqlite3_finalize(statement);
}
#end
Here is what i have done:
change the folder and path permissions using CHMOD
split the sqlite3_exec() into sqlite3_prepare(), sqlite3_step() and sqlite3_finalize() - I get the same output - query is created but record is not created in database
I am able to retrieve record information
You can't write to a database in the resource bundle. You need to copy it and then use it.
Here's some code I've successfully used to do that (key is ensureDatabasePrepared where it is copied from resources):
- (BOOL)ensureDatabaseOpen: (NSError **)error
{
// already created db connection
if (_contactDb != nil)
{
return YES;
}
NSLog(#">> ContactManager::ensureDatabaseOpen");
if (![self ensureDatabasePrepared:error])
{
return NO;
}
const char *dbpath = [_dbPath UTF8String];
if (sqlite3_open(dbpath, &_contactDb) != SQLITE_OK &&
error != nil)
{
*error = [[[NSError alloc] initWithDomain:#"ContactsManager" code:1000 userInfo:nil] autorelease];
return NO;
}
NSLog(#"opened");
return YES;
}
- (BOOL)ensureDatabasePrepared: (NSError **)error
{
// already prepared
if ((_dbPath != nil) &&
([[NSFileManager defaultManager] fileExistsAtPath:_dbPath]))
{
return YES;
}
// db in main bundle - cant edit. copy to library if !exist
NSString *dbTemplatePath = [[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"db"];
NSLog(#"%#", dbTemplatePath);
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
_dbPath = [libraryPath stringByAppendingPathComponent:#"contacts.db"];
NSLog(#"dbPath: %#", _dbPath);
// copy db from template to library
if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
{
NSLog(#"db not exists");
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:&error])
{
return NO;
}
NSLog(#"copied");
}
return YES;
}
If you are interacting with the database only from the code you posted - you are missing a sqlite3_close. Most likely the changes are not getting flushed onto disk
I am building an app which contains a form in one view,in which the user fills all the fields and when he clicks the save button the data must be saved in to database and after navigating back,there's another view which, when entered, must show the saved data(event).
I have created a database and have gone through several sqlite3 tutorials;
I have done all other changes to my code according to my requirement. However, when I use this statement to check whether data is inserted in database:
SELECT * FROM reminders;
I am getting nothing and I am confused whether data is inserted or not.
How do I save it properly, and how do I retrieve data from database to use and display it in other view?
First you should create the sqlite3 database file (check this link), then you should include it into your project. Now to connect to it you can use the following code:
#pragma mark -
#pragma mark Create/Load Database
+ (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
BOOL success;
NSFileManager * fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) {
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSError * error;
NSString * defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"DATABASENAME.DB"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
+ (sqlite3 *)getDBConnection {
[DatabaseController createEditableCopyOfDatabaseIfNeeded];
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
// Open the database. The database was prepared outside the application.
sqlite3 * newDBConnection;
if (sqlite3_open([path UTF8String], &newDBConnection) == SQLITE_OK) {
//NSLog(#"Database Successfully Opened :)");
} else {
//NSLog(#"Error in opening database :(");
}
return newDBConnection;
}
then to insert a record you can use this code:
+ (void)insertEvent:(Event *)newEvent {
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "INSERT INTO Event (Serial, Name, Date) VALUES (?, ?, ?)";
sqlite3_stmt * insert_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &insert_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// Error
sqlite3_close(connection);
return;
}
sqlite3_bind_int(insert_statement, 1, newEvent.Serial);
sqlite3_bind_text(insert_statement, 2, [newEvent.Name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(insert_statement, 3, [newEvent.Date timeIntervalSince1970]);
int statement_result = sqlite3_step(insert_statement);
if ((statement_result != SQLITE_DONE) && (statement_result != SQLITE_OK)) {
//Error
sqlite3_close(connection);
return;
}
sqlite3_finalize(insert_statement);
// Get the Id of the inserted event
int rowId = sqlite3_last_insert_rowid(connection);
newEvent.Id = rowId;
sqlite3_close(connection);
}
now to get an event:
+ (Event *)getEventById:(int)id {
Event * result = nil;
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "SELECT * FROM Event WHERE Id = ?";
sqlite3_stmt * select_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &select_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// error
sqlite3_close(connection);
return result;
}
sqlite3_bind_int(select_statement, 1, id);
if (sqlite3_step(select_statement) == SQLITE_ROW) {
result = [[[Event alloc] init] autorelease];
result.Id = sqlite3_column_int(select_statement, 0);
result.Serial = sqlite3_column_int(select_statement, 1);
result.Name = (((char *) sqlite3_column_text(select_statement, 2)) == NULL)? nil:[NSString stringWithUTF8String:((char *) sqlite3_column_text(select_statement, 2))];
result.Date = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(select_statement, 3)];
}
sqlite3_finalize(select_statement);
sqlite3_close(connection);
return (result);
}
Here is a blog post that should get you pointed in the right direction, pretty useful to me so sharing it with you.:P
http://dblog.com.au/iphone-development-tutorials/iphone-sdk-tutorial-reading-data-from-a-sqlite-database/
you can check whether your data has been saved or not by checking the database table. Go to Users>your computer name>Library>Application support>iphone Simulator>4.3(your version of ios)>Application.. Then look for your Application,go to documents and open the sqlite file. Here you can see the data.
You should use FMDB to reduce the complexity of your code.
It is an Objective-C wrapper around SQLite.
FMDB on github
This code used for storing data and retriving data from sqlite data base
First you just add sqlite3 frame work after that write bellow code in objective-c
ViewController.h
#import <UIKit/UIKit.h>
#import "sqlite3.h"
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *firstName;
#property (weak, nonatomic) IBOutlet UITextField *lastName;
#property (weak, nonatomic) IBOutlet UITextField *state;
#property (weak, nonatomic) IBOutlet UITextField *mobileNum;
- (IBAction)saveButton:(id)sender;
- (IBAction)featchButton:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *label;
#property NSString *myDatabase;
#property sqlite3 *marksDB;
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *docsDir;
NSArray *dirPaths;
dirPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir=dirPaths[0];
_myDatabase=[[NSString alloc]initWithString:[docsDir stringByAppendingString:#"marks.db"]];
NSLog(#"My Data base %#",_myDatabase);
NSFileManager *fileMgr=[NSFileManager defaultManager];
if ([fileMgr fileExistsAtPath:_myDatabase]==NO)
{
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
char *errMsg;
const char *sql_stmt="CREATE TABLE IF NOT EXISTS MARKS(ID INTEGER PRIMARY KEY AUTOINCREMENT ,FIRST NAME TEXT,LAST NAME TEXT,STATE TEXT,MOBILE INTEGER )";
if (sqlite3_exec(_marksDB, sql_stmt, NULL, NULL, &errMsg)!=SQLITE_OK)
{
_label.text=#"Failed to create Table";
}
sqlite3_close(_marksDB);
}
else
{
_label.text=#"Failed to Create/Open Database";
}
}
}
- (IBAction)saveButton:(id)sender {
sqlite3_stmt *statement;
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *insertSQL=[NSString stringWithFormat:#"INSERT INTO MARKS(firstname,lastname,state,mobile )VALUES(\"%#\",\"%#\",\"%#\",\"%#\")",_firstName.text,_lastName.text,_state.text,_mobileNum.text ];
const char *insert_stmt=[insertSQL UTF8String];
sqlite3_prepare_v2(_marksDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement)==SQLITE_DONE)
{
_label.text=#"Contact Added";
_firstName.text=#"";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
else
{
_label.text=#"Failed to Add Contact";
}
sqlite3_finalize(statement);
sqlite3_close(_marksDB);
}
}
- (IBAction)featchButton:(id)sender {
const char *dbpath=[_myDatabase UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *query=[NSString stringWithFormat:#"SELECT firstname,lastname,state,mobile, FROM MARKS WHERE firstname=\"%#\"",_firstName.text];
const char *query_stmt=[query UTF8String];
if (sqlite3_prepare_v2(_marksDB, query_stmt, -1, &statement, NULL)==SQLITE_OK)
{
if (sqlite3_step(statement)==SQLITE_ROW)
{
NSString *first=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
_firstName.text=first;
NSString *lastName=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
_lastName.text=lastName;
NSString *state=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
_state.text=state;
NSString *mobile=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
_mobileNum.text=mobile;
_label.text=#"Match Found";
}
else
{
_label.text=#"Not Matched";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
sqlite3_finalize(statement);
}
sqlite3_close(_marksDB);
}
}
#end
I have a problem with my app it opens this database and selects rows from it ok,
Then when I want to add new rows using the following code and I always get the following problem at the execution of the prepare_V2.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while creating add statement. 'out of memory''
code is .....
static sqlite3 *database = nil;
static sqlite3_stmt *addStmt = nil;
- (BOOL)addUserprofile {
addStmt = nil; // set to force open for testing
database = nil; // set to force creation of addstmt for testing
if (database == nil) { // first time then open database
NSString *databaseName = #"UserProfile.db";
// Use editable database paths
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"path = %#",databasePath);
NSLog(#"opening Database");
sqlite3 *database;
// Open the database from the users filessytem
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"Database Open");
}
else {
NSLog(#"Database did not open");
}
}
if(addStmt == nil) {
NSLog(#"Creating add stmt");
const char *sql = "INSERT INTO Profile (ProfileName) VALUES(?)";
if(sqlite3_prepare_v2(database, sql, -1, &addStmt, NULL) != SQLITE_OK) {
NSAssert1(0, #"** Error while creating add statement. '%s'", sqlite3_errmsg(database));
success = NO;
return success;
}
}
sqlite3_bind_text(addStmt, 1, [ProfileName UTF8String], -1, SQLITE_TRANSIENT);
Sometime, your database is being SQLITE_BUSY or SQLITE_LOCKED.
You can refer this framework to know how to do:
https://github.com/ccgus/fmdb
Good luck!:)
i've been trying to figure out wh. sqy my object allocation keeps rigth up every time i call this function, Instruments reports no leaks but I get a heck of a lot of object coming from
sqlite3_exec --> sqlite3Prepare --> sqlite3Parser --> yy_reduce --> malloc & also a whole bunch from
& from
sqlite3Step --> sqlite3VdbeExec --> sqlite3BtreeInsert --> malloc
I tried solving it by following the suggestions posted here: http://www.iphonedevsdk.com/forum/iphone-sdk-development/7092-sqlite3-database-gobbling-up-memory.html but haven't been able to fix it
ANY HELP is appreciated, my code is below
+(void)getDesignationsInLibrary:(NSString *)library
{
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSString *dbName = #"s8.sqlite";
NSArray *documentPaths = \
NSSearchPathForDirectoriesInDomains \
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = \
[documentPaths objectAtIndex:0];
NSString *databasePath = \
[documentsDir stringByAppendingPathComponent:dbName];
[[DT sharedDT].designationsInLibrary removeAllObjects];
NSString *sqlString;
for(int i=0;i<[[DT sharedDT].typesInLibrary count];i++)
{
if(sqlite3_open([databasePath UTF8String], &db)==SQLITE_OK)
{
if (sqlite3_exec(db, "PRAGMA CACHE_SIZE=50;", NULL, NULL, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to set cache size with message '%s'.", sqlite3_errmsg(db));
}
NSMutableString *lib=[NSMutableString stringWithString:library];
[lib appendString:#"-"];
[lib appendString:[[DT sharedDT].typesInLibrary objectAtIndex:i]];
if([DT sharedDT].sortedBy==#"AISC Default")
{
sqlString = [NSString stringWithFormat:#"select DESIGNATION from \"%#\";",lib];
}
else
{
sqlString = [NSString stringWithFormat:#"select DESIGNATION from \"%#\" order by cast(%# as numeric) %#;",lib, [DT sharedDT].sortedBy, [DT sharedDT].sortAscDesc];
}
const char *sql = [sqlString cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(db,sql,-1,&selectstmt, NULL)==SQLITE_OK)
{
while(sqlite3_step(selectstmt)==SQLITE_ROW)
{
[[DT sharedDT].designationsInLibrary addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,0)]];
}
sqlite3_finalize(selectstmt);
selectstmt=nil;
}
}
}
sqlite3_close(db);
[localPool release];
}
It seems, that you're opening db on every loop cycle, but close only once, before function exit
So try to change:
}
sqlite3_close(db);
[localPool release];
}
to
sqlite3_close(db);
}
[localPool release];
}
Or even better change:
for(int i=0;i [[DT sharedDT].typesInLibrary count];i++)
{
if(sqlite3_open([databasePath UTF8String], &db)==SQLITE_OK)
{
if (sqlite3_exec(db, "PRAGMA CACHE_SIZE=50;", NULL, NULL, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to set cache size with message '%s'.", sqlite3_errmsg(db));
}
to:
if(sqlite3_open([databasePath UTF8String], &db)==SQLITE_OK)
{
if (sqlite3_exec(db, "PRAGMA CACHE_SIZE=50;", NULL, NULL, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to set cache size with message '%s'.", sqlite3_errmsg(db));
}
for(int i=0;i [[DT sharedDT].typesInLibrary count];i++)
{
...
because you're always open the same database
Try invoking sqlite3_exec with:
pragma cache_size=1
Sqlite seems to gobble up memory for caching.
I was successfully accessing my database to get a list of cities on the App launch. I tried running a second query against it right afterward to get the list of States but all that happens is that my app blows up with no usable error in the console (simply says "Program received signal: EXEC_BAD_ACCESS" and nothing more).
Here is the code, I was hoping someone could potentially explain to me what I'm doing wrong:
-(void) initializeDatabase{
// The database is stored in the application bundle
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK){
[self initializeCities:database];
[self initializeStates:database];
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
-(void) initializeCities:(sqlite3 *)db {
NSMutableArray *cityArray = [[NSMutableArray alloc] init];
self.cities = cityArray;
[cityArray release];
// Get the primary key for all cities.
const char *sql = "SELECT id FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
int primaryKey = sqlite3_column_int(statement, 0);
City *city = [[City alloc] initWithPrimaryKey:primaryKey database:db];
[cities addObject:city];
[city release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
-(void) initializeStates:(sqlite3 *)db {
NSMutableArray *statesArray = [[NSMutableArray alloc] init];
self.states = statesArray;
[statesArray release];
// Get the primary key for all cities.
const char *sql = "SELECT DISTINCT state FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
// We "step" through the results - once for each row
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state;
state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
I can't debug this code as the debugger never hits my breakpoints at all.
If I remove the initializeStates method the app works as expected (albiet without a list of states).
You are releasing "state" without having allocated it. Try something like this:
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state = [[NSString alloc] initWithCString:(char*)sqlite3_column_text(statement, 0) encoding:NSASCIIStringEncoding];
//state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
Update: add cast above to fix compiler warning
Your problem is this:
NSString *state = (NSString *)sqlite3_column_text(statement, 0);
According to the documentation, sqlite3_column_text() returns a char*, not an NSString*.
Edit: You wouldn't have had this problem if you'd have used a wrapper ;)