UPDATED QUESTION, check the end of the post
I am making an iPhone app and I want when the user first lunches the app, for the app to create a new database and populate it with the data of a plist file.
Here is my view did load code:
//get database path
NSString *symptomDatabasePath = [self symptomsDatabasePath];
//check if database exists and initialize if it doesn't
if(![[NSFileManager defaultManager] fileExistsAtPath:symptomDatabasePath])
{
[self createAndPopulateDatabase];
}
My symptomDatabasePath function:
//get the path of the symptoms database
- (NSString *) symptomsDatabasePath
{
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsArray objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"symptoms.sqlite"];
}
And finally my createANdPopulateDatabase function:
- (void) createAndPopulateDatabase
{
//get query to insert symptoms into database
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsArray objectAtIndex:0];
NSString *databaseDataFile = [documentsDirectory stringByAppendingPathComponent:#"symptomsDatabaseList.plist"];
NSString *symptomData = [NSString stringWithContentsOfFile:databaseDataFile encoding:NSUTF8StringEncoding error:NULL];
sqlite3 *database;
if (sqlite3_open([[self symptomsDatabasePath] UTF8String], &database)!=SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Failed to open database");
}
NSString *createTableQuery = #"CREATE TABLE IF NOT EXISTS tbl_symptoms (symptomCode varchar(15) NOT NULL PRIMARY KEY,title varchar(255) NOT NULL,shortTitle varchar(255) NOT NULL,inclusions varchar(255) NOT NULL,exclusions varchar(255) NOT NULL,symptomCategory varchar(255) NOT NULL);";
char *errorMsg;
//create new table
if(sqlite3_exec(database, [createTableQuery UTF8String], NULL, NULL, &errorMsg)!=SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Error creating table: %s", errorMsg);
}
NSString *insertQueryString = #"INSERT INTO tbl_symptoms (symptomCode, title, shortTitle, inclusions, exclusions, symptomCategory) VALUES ";
//array containing the seperate symptom data
NSArray *symptomArray = [symptomData componentsSeparatedByString:#"),"];
sqlite3_stmt *statement;
//nsmutable array with all queries
NSMutableArray *queryArray = [[NSMutableArray alloc] init];
//loop through the aray with symptoms build queries and store them in an array
for(int i=0; i<[symptomArray count]; i++)
{
//create query string with insert command
NSMutableString *insertString = [[NSMutableString alloc] initWithString:insertQueryString];
//append the specific symptoms data
[insertString appendString:[symptomArray objectAtIndex:i]];
[insertString appendString:#");"];
[queryArray insertObject:insertString atIndex:i];
}
//insert data into database
for(int i=0; i<[queryArray count]; i++)
{
const char *insertChar = [[queryArray objectAtIndex:i] UTF8String];
if(sqlite3_prepare_v2(database, insertChar, -1, &statement, nil)!=SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Error filling table: %s", errorMsg);
}
if (sqlite3_step(statement)!=SQLITE_DONE)
{
NSAssert(0, #"Error inserting data into database: %s", errorMsg);
}
//finalize changes
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
My code is a bit messy because i have been trying various solutions. Anyway I have the data I want to input written like this in my phlist file:
('Z27', 'Fear of a social problem', 'Fear of a social problem', 'concern about/fear of having a social problem', 'if the patient has a social problem, code the problem ', 'Social problems’),
('Z28', 'Limited function/disability (Z)', 'Limited function/disability (Z)', '', '', 'Social problems’),
('Z29', 'Social problem NOS', 'Social problem NOS', 'environmental problems; malingering', '', 'Social problems’
there are 320 such entries in my file, these are the last 3 because they are the ones I am having trouble with.
The app copies all the other 317 entries, but not these 3. And I know it is missing a ) at the end, but that's because my code appends it on every loop.
I get a SIGABRT error and the following error code:
"Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error filling table: (null)'"
Can anyone help me? I probably have done something wrong with sqlite3, though I don't understand why it only doesn't copy the last 3 entries.
Also earlier it wouldn't copy only the last entry, but after I copy pasted it around to see if it was a problem with the entry itself, it started not copying the last 3 :S.
Any help will be greatly appreciated
UPDATE
So it turns out I was getting the errors cause I was using NSAssert. Now I am using NSLogs to log my errors, and the app launches correctly, but I still get the error when copying the final 3 entries.
According to my logs moth the sqlite3_prepare_v2 and the sqlite3_step functions fail, but ONLY for the final 3 entries :S
The problem is that you have smart quotes. For example, I see you have
..., 'Social problems’), ...
Note, you're starting that with a standard apostrophe, but finishing it with a closing single quote. If you look very carefully, they look different.
Replace those smart quotes with standard apostrophe and that should fix the immediate problem.
I'd suggest you log the SQL and carefully examine it. Also, when the sqlite3_prepare_v2 fails, you are not logging the appropriate error message. You can do something like:
if (sqlite3_prepare_v2(database, insertChar, -1, &statement, nil)!=SQLITE_OK) {
NSLog(#"prepare failure: %s", sqlite3_errmsg(database));
sqlite3_close(database);
NSAssert(0, #"Error filling table");
}
Related
It's my first time trying to develop an SQLite database for IOS, or any IOS app for that matter. I'm trying to follow a tutorial I found online and adapt it for my own use. The database was created without any issues but my Insert statement never seems to return my error message.
Nothing appears in the console as nothing drastic actually goes wrong with the program. If you need any more information I'll try my best to find it and update the question with it.
Here is my code:
// Method to store a GPS location
-(void)insertGPS:(GPS*)GPS
{
// Create insert statement for the person
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO GPSJob (jobNo, sourceMonitor, positionNumber, latitude, longitude) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\")", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude ];
// Define an error
char *error;
// Attempt to execute the insert statement
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"GPS inserted into database with values: %#, %#, %#, %#, %#.", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude);
}
// If the insert statement is not okay
else {
NSLog(#"Error: %s", sqlite3_errmsg(databaseHandle));
}
}
Here is the tutorial I'm following: http://www.apptite.be/tutorial_ios_sqlite.php
The updated error message said this :
2015-08-12 15:28:25.299 NoiseApp[7602:207] Error: out of memory
-----------------------------------Solution----------------------------------
For anyone who wants the solution to this problem I simply amended the function to read as follows :
// Method to store a GPS location
-(void)insertGPS:(GPS*)GPS
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"GPS.db"];
if(sqlite3_open([databasePath UTF8String], &databaseHandle) ==SQLITE_OK){
// Create insert statement for the person
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO GPSJob (jobNo, sourceMonitor, positionNumber, latitude, longitude) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\")", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude ];
// Define an error
char *error;
// Attempt to execute the insert statement
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"GPS inserted into database with values: %#, %#, %#, %#, %#.", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude);
}
// If the insert statement is not okay
else {
NSLog(#"Error: %s", sqlite3_errmsg(databaseHandle));
}
}
}
databaseHandle is not defined in your function. You should get your reference to the sqlite3_open somewhere.
Use sqlite3_open to open your database, if you didn't do this already.
Example of opening. (note if you want to write to this database you should copy it from the resourcePath to somewhere writeable, and open this version)
databaseName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"DB.sql"];
if(sqlite3_open([databaseName UTF8String],&databaseHandle) == SQLITE_OK)
{
}
Below is my code, I'm attempting to retrieve data from an sqlite database with airport city names query from a user entered text field and retrieve the ICAO identifier to be presented in a label. It seems the db is loading but it will not query when I select the IBAction button. I think there might be something wrong with my query statement or my database, although I can't list that on here. Any Help would be greatly appreciated.
The Last error I received is: database3[30351:c07] -[ViewController searchICAO:] 1st SQL error 'library routine called out of sequence' (21)
-(NSString*)filePath {
NSArray*paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0]stringByAppendingPathComponent:#"mydatabase.sqlite"];
}
//open database
- (void)viewDidLoad{
[self openDB];
}
-(void)openDB {
if(sqlite3_open([[self filePath]UTF8String], &airportDB) !=SQLITE_OK) {
sqlite3_close(airportDB);
NSAssert(0, #"Databese failed to open");
status.text = #"Database Failed to Open";
}
else if (sqlite3_open([[self filePath]UTF8String], &airportDB) ==SQLITE_OK) {//this line not really needed but was trying everything
NSLog(#"database opened"); //test
status.text = #"Database Opened"; //test
}
}
- (IBAction)searchICAO:(id)sender
{
//[self.delegate detailViewControllerDidFinish:self]; //for later use
//Get airport name from the text field user enters
NSString*sql = [NSString stringWithFormat:#"SELECT * FROM airports WHERE city=\"%#\"", [searchDB text]];
const char *query_stmt = [sql UTF8String];
sqlite3_stmt *statement;
NSLog(#"%s 1st SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(airportDB), sqlite3_errcode(airportDB)); //Error Test
This is where I seem to be having problems at...
if (sqlite3_prepare_v2(airportDB, query_stmt, -1, &statement, NULL)==SQLITE_OK) {// Problem is from here, can't get past this point
NSLog(#"%s 2nd SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(airportDB), sqlite3_errcode(airportDB)); //Error Test
if (sqlite3_step(statement)==SQLITE_ROW) {
status.text = #""; //Clear the status line
NSString *returnICAO = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
status.text = returnICAO; //Insert Airport ICAO letters from the database table
}
sqlite3_finalize(statement);
[super viewDidLoad];
}
sqlite3_close(airportDB);
}
I am not seeing exactly what is going wrong, but I would offer that, unless you have a specific objective that cannot be met by using third-party code, you should consider using FMDB https://github.com/ccgus/fmdb - assuming that you need to go directly to SQLite. I have used it quite a bit and had good success with it.
It's a little strange to be "closing" the DB and calling [super viewDidLoad] from searchICAO:. Was this deliberate? It seems like this might be the source of your problem? On the second query, the DB will be closed?
I need to develop a sql statement based on values picked on a UIPickerView. If you need a visual idea, here's a link to the screenshot (sorry not enough reputation to post pics yet) . I haven't been able to find any documentation on this and want to make sure I'm on the right track before I dig into it.
Each component (kTypeComponent, kDifficultyComponent, kDescriptionComponent) has three rows to select from (ex. kTypeComponent row1=bike, row2=run, row3=swim)
My thought would be that the sql statement would look something like this
sqlite3_stmt *pickerStatement;
//This would give back a string of the row selected (i.e bike, run, swim)
NSInteger getTypeSelected = [pickerView selectedRowInComponent:kTypeComponent];
NSString typeSQL = [rowOneItems objectAtIndex:getTypeSelected];
const char *pickerSQL = "SELECT description FROM workoutTbl WHERE (type = typeSQL) AND ...
Is this possible to do with a sql statement? I'm only familiar with basic SQL, so I'm not sure
Would the SQL statement go in the action (button) or where I set up my NSMutableArray and open the database? Should it go into a different class?
Edit - Solution
In case anyone comes around with the same problem, here is the solution to it
- (NSArray *)getWorkoutListwithType:(NSString *)workoutType withDifficulty:(NSString *)difficulty withLength:(NSString *)length {
NSMutableArray *workouts;
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"workoutList.sqlite"];
// NSLog(#"Db path is %#",dbPath);
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if (!success){
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if (!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK)) {
NSLog(#"error with message '%s'.", sqlite3_errmsg(db));
}
// only alloc/init the array if the SQL database opens properly
workouts = [[NSMutableArray alloc] init];
sqlite3_stmt *sqlStatement;
// add "%%" as a wildcard so the query will say "difficulty LIKE '>30%' and match >30 MINS, >30 HOURS, etc.
NSString *sqlString = [NSString stringWithFormat: #"SELECT description FROM workoutTbl WHERE type LIKE '%#%%' AND difficulty LIKE '%#%%' AND duration LIKE '%#%%'", workoutType, difficulty, length];
NSLog(#"query: %#", sqlString);
const char *sql = [sqlString UTF8String];
if (sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK) {
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
}
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
[workouts addObject:[NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,0)]];
}
sqlite3_finalize(sqlStatement);
}
#catch (NSException *exception) {
NSLog(#"An exception occured: %#", [exception reason]);
}
#finally {
sqlite3_close(db);
}
// Pass back an immutable copy of the array. if the array is nil, then the database never opened and there will be an error
return [workouts copy];
}
What do you mean by 'three rows to select'? Do you mean 'three fields (columns) to select'? If you want to specify field values, then a statement should like
NSString* sqlStatement = [NSString stringWithFormat:#"SELECT * FROM workoutTbl WHERE type = '%#' AND id = '%i'", typeSQL,idNumber];
I am trying to read data from a sqlite database in an ios app. When I run the app, it is able to open the database but in the log file it shows the message - "Problem with the prepare statement". I don't know what is wrong with my prepare statement Here's my code -
-(NSString *)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
In the viewDidLoad I have -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myarray = [[NSMutableArray alloc]init];
sqlite3 *database;
if(sqlite3_open([[self dataFilePath]UTF8String], &database)!=SQLITE_OK){
sqlite3_close(database);
NSAssert(0, #"Failed to open database");
}
const char *createSQL = #"SELECT ID, TITLE FROM FIRST ORDER BY TITLE;"; //first is the table in the database
sqlite3_stmt *sqlStmt;
if(sqlite3_prepare_v2(database, [createSQL UTF8String], -1, &sqlStmt, nil)!=SQLITE_OK){
NSLog(#"Problem with prepare statement"); //this is where the code gets stuck and I don't know why
}else{
while(sqlite3_step(sqlStmt)==SQLITE_ROW){
NSInteger number = sqlite3_column_int(sqlStmt, 0);
NSString *title = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStmt, 1)];
[myarray addObject:title];
}
sqlite3_finalize(sqlStmt);
}
sqlite3_close(database);
}
If your prepare statement fails, rather than just reporting "Problem with prepare statement", try retrieving the error message, e.g.,
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(database), sqlite3_errcode(database));
This might give you a better indication of the problem.
A problem I've seen in the past is that the database might not be found (because it wasn't included in the bundle, typo in the name, etc.) but the standard sqlite3_open function will create it if it's not there, and thus the sqlite3_open will succeed, but the table in question won't be found in the blank, newly created database. Better than sqlite3_open would be:
sqlite3 *database;
if (sqlite3_open_v2([[self dataFilePath] UTF8String], &database, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
sqlite3_close(database); // not sure you need to close if the open failed
NSAssert(0, #"Failed to open database");
}
That way you get a warning if the database is not found. But if you've done sqlite3_open already, you might have a blank database, so you might want to reset your simulator and/or remove the app from your device, before trying it with sqlite3_open_v2.
Several things you can try.
Clean your app, Remove from simulator or device & try installing a fresh copy again and see if it works.
Open your DB in Terminal & try to run your sql statement at there. Check wether you are getting desired output.
Try changing the nil to NULL. Also, try defining the SQL statement as a const char.
....
const char *sql = "SELECT ID, TITLE FROM FIRST ORDER BY TITLE";
sqlite3_stmt *sqlStmt;
if(sqlite3_prepare_v2(database, sql, -1, &sqlStmt, NULL)!=SQLITE_OK){
....
I working on an app that takes input from a text field and puts it into a string. I have a table with a field in it that I want to check the value of the string from the input against the value in the field in the database. I'm new to iOS and fairly new to SQLite.
Code:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
lblStoreNumber.text = strStoreNumber;
lblRegNumber.text = strRegNumber;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths lastObject];
// NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"tblStore.sqlite"];
NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opened sqlite database at %#", databasePath);
//...stuff
}
else
{
NSLog(#"Failed to open database at %# with error %s", databasePath, sqlite3_errmsg(database));
sqlite3_close (database);
}
NSString *querystring;
// create your statement
querystring = [NSString stringWithFormat:#"SELECT strStore FROM tblStore WHERE strStore = %#;", strStoreNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK) //queryString = Statement
{
NSLog(#"sql problem occured with: %s", sql);
NSLog(#"%s", sqlite3_errmsg(database));
}
else
{
// you could handle multiple rows here
while (sqlite3_step(databasePath) == SQLITE_ROW) // queryString = statement
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 1)];
} // while
}
sqlite3_finalize(databasePath);
// Do something with data...
}
It gets to the line "NSLog(#"Opened sqlite database at %#", databasePath);", so it appears as though it has access to the database. However, when I run the app, I get the "NSLog(#"sql problem occured with: %s", sql);" error, which I can see in the console. Additionally, in the console, it says "No such table: tblStore".
I created the table using the Firefox add-on SQLite Manager. I added the sqlite3 library to the project. I dragged and dropped the database table I created in SQLite manager into my project, above my two AppDelegate files and my two ViewController files.
Any help or input would be greatly appreciated. Thanks!
EDIT: I have properly added the file to the project, and it appears as though the table is found now. Now I have some strange warnings, though:
"Incompatible pointer types passing 'const char *' to parameter of type 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *')"
This warning appears on the following lines of code:
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK)
while (sqlite3_step(sql) == SQLITE_ROW)
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 1)];
sqlite3_finalize(sql);
It's got something to do with "sql", but I'm unsure of what. Any suggestions?
Your code seems ok - did you copy the db to the ressource folder of your project?
EDIT
Make sure you access your db file with something like that:
- (void) initializeDB {
// Get the database from the application bundle
NSString* path = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opening Database");
}
else
{
// Call close to properly clean up
sqlite3_close(database);
NSAssert1(0, #"Error: failed to open database: '%s'.",
sqlite3_errmsg(database));
}
}
The database file you add to the project will be embedded in the main NSBundle (see [NSBundle mainBundle]).
In order to do what you want, you need to copy the database from the main bundle to the documents folder before trying to access it. Otherwise, as you are experiencing, you will not be able to find the SQLite DB on the document's folder.
You can copy your database, click finder and write this address(/Users/administrator/Library/Application Support/iPhone Simulator/6.1/Applications/) in finder click ok.
You will get documentary path.
Open your project document file and paste your database....