I am using the firefox sqlite manager to help me building an iphone app. In which I include an UPDATE query, which works perfectly in the iPhone simulator. However, it fails when I run from the real machine (the iphone). There is no error, it just does not update the db.
I am thinking of two possible causes:
1) The db it updates is not the one as in simulator
2) The db I read is not the one that is updated
Does anyone have similar experience?
Code as follows:
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"Exercises.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"An error has occured.");
}
NSString *ranID = [#"UPDATE Status SET money = " stringByAppendingFormat:#"%d", money + 100];
const char *sql2 = [ranID UTF8String];
sqlite3_stmt *sqlStatement2;
if(sqlite3_prepare(db, sql2, -1, &sqlStatement2, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement 2");
}
if (sqlite3_step(sqlStatement2)==SQLITE_ROW)
NSLog(#"succeed");
Thanks in advance.
What happening is that you are trying to write inside the database that is in your bundle, you dont have write access to files in your bundle, you will need to copy it to Documents directory if you want to update the database
To copy the database file
NSString *documentsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Exercises.sqlite"];
[[NSFileManager defaultManager] copyItemAtPath:dbPath toPath:documentsPath error:NULL];
Then always use the new file path (documentsPath)to access the database
Files in your app's bundle are read-only on device.
You should copy it to somewhere in your app's sandbox first, such as in your Library/Application Support directory. (You can use - [NSFileManager URLsForDirectory:inDomains:] with NSApplicationSupportDirectory to find this path; be sure to create the directory before you try to write to it.)
This sounds like you are not committing our changes. See similar posts here and here
I'm trying to create an SQLite3 database file through Objective-C at run time. I am trying to create a table called "tblStore". I want the field names to be called "strStoreNumber" and "strStoreReg". I'm new to iOS and SQLite, so I'm having a hard time finding the syntax to perform this. In addition to creating the table, I want the created table to reside NOT in the app bundle, but rather it would reside/be stored somewhere on the phone. The table needs to be readable/writeable. I've done some reading on "user sandbox" and also a "documents directory". I'm not sure I understand the difference between the two. Ideally, my app would use a button to take input from Text Fields. After the input from the texts fields is put into strings, a check would be done to see if my "tblStore" SQLite table exists, and if it doesn't, the table will be created.
To recap:
1. What is the syntax for Obj-C/SQLite to create a table called "tblStore" with the fields "strStoreNumber" and "strStoreReg"?
2. Where should the db file reside? I need to read from and write to the tblStore db file.
3. What is the difference between a "user sandbox" and a "documents directory"?
This is what I have currently:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
NSString *tableName = #"tblStore";
NSString *dbStrStore = #"strStore";
NSString *dbStrReg = #"strReg";
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
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);
char *err;
NSString *sql = [NSString stringWithFormat:#"CREATE TABLE IF NOT EXISTS '%#' ('%#' TEXT PRIMARY KEY, '%#' TEXT);", tableName, dbStrStore, dbStrReg];
if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Table failed to create.");
}
//...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, strReg FROM tblStore WHERE strStore = %# AND strReg = %#;", strStoreNumber, strRegNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
sqlite3_stmt *statement = nil;
if (sqlite3_prepare_v2(database, sql, -1, &statement, 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(statement) == SQLITE_ROW)
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
}
}
sqlite3_finalize(statement);
lblStoreNumber.text = szStore;
lblRegNumber.text = szReg;
}
When I run my app, I get the following errors:
2012-05-10 14:58:38.169 CCoDBTry[355:f803] Opened sqlite database at /Users/Matt****/Library/Application Support/iPhone Simulator/5.1/Applications/5DB7A218-A0F6- 485F-B366-91FD2F9BC062/Documents/tblStore.sqlite
2012-05-10 14:58:38.307 CCoDBTry[355:f803] sql problem occured with: SELECT strStore, strReg FROM tblStore WHERE strStore = 8053 AND strReg = 4;
2012-05-10 14:58:38.308 CCoDBTry[355:f803] no such column: strStore
I appreciate anyone who takes the time out to explain some of this stuff, as I am very new and have been unsuccessful in accomplishing some of the things I've tried. Thanks so much for the help!
// Create DB
-(NSString *) filePath
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory=[paths objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"LoginDatabase.sql"];
}
// Open DB
-(void)openDB
{
if(sqlite3_open([[self filePath]UTF8String], &db) !=SQLITE_OK)
{
sqlite3_close(db);
NSAssert(0, #"Database failed to Open");
}
}
// Create Table
-(void) createTableNamed:(NSString*)tableName withField1:(NSString*) field1 withField2:(NSString*) field2
{
char *err;
NSString *sql=[NSString stringWithFormat:#" CREATE TABLE IF NOT EXISTS '%#'('%#' TEXT PRIMARY KEY,'%#' TEXT);",tableName,field1,field2];
if(sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) !=SQLITE_OK)
{
sqlite3_close(db);
NSAssert(0, #"Table failed to create");
}
}
// Inserting records
-(void)insertrecordIntoTable:(NSString*) tableName withField1:(NSString*) field1 field1Value:(NSString*)field1Vaue andField2:(NSString*)field2 field2Value:(NSString*)field2Value
{
NSString *sqlStr=[NSString stringWithFormat:#"INSERT INTO '%#'('%#','%#')VALUES(?,?)",tableName,field1,field2];
const char *sql=[sqlStr UTF8String];
sqlite3_stmt *statement1;
if(sqlite3_prepare_v2(db, sql, -1, &statement1, nil)==SQLITE_OK)
{
sqlite3_bind_text(statement1, 1, [field1Vaue UTF8String], -1, nil);
sqlite3_bind_text(statement1, 2, [field2Value UTF8String], -1, nil);
}
if(sqlite3_step(statement1) !=SQLITE_DONE)
NSAssert(0, #"Error upadating table");
sqlite3_finalize(statement1);
}
// Retrieve data from table
-(void)getAllRowsFromTableNamed:(NSString *)tableName
{
NSString *field1Str,*field2Str;
NSString *qsql=[NSString stringWithFormat:#"SELECT * FROM %#",tableName];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
while(sqlite3_step(statement) ==SQLITE_ROW)
{
char *field1=(char *) sqlite3_column_text(statement, 0);
char *field2=(char *) sqlite3_column_text(statement, 1);
field1Str=[[NSString alloc]initWithUTF8String:field1];
field2Str=[[NSString alloc] initWithUTF8String:field2];
NSString *str=[NSString stringWithFormat:#"%# - %#",field1Str,field2Str];
NSLog(#"%#",str);
}
}
}
In viewDidLoad call the methods
- (void)viewDidLoad
{
[self openDB];
[self createTableNamed:#"Login" withField1:#"USERNAME" withField2:#"PASSWORD"];
[self insertrecordIntoTable:#"Login" withField1:#"USERNAME" field1Value:username andField2:#"PASSWORD" field2Value:password];
}
Where username and password are NSString values;
sqlite is a pain if you dont know what you're doing. I also had some problems with sqlite c functions but then i decided to use sqlite wrapper.
FMDB and BWDB are good and easy to use sqlite wrappers for objective c. I suggest you use one of those .
Note that BWDB is in a lynda.com tutorial (this one) and if you don't find it on the web...leave a comment and i'll upload it somewhere.
edit: the only place you can write stuff in your app is in your documents directory...so..it plain terms...if the db is not in your documents directory..is read-only..also..when you read/write to your db..the OS copies the db in the documents directory..and does all the reading and writing there so you can have a db in your app bundle but you can't edit that one...so you'll end up with 2 db.I had the same problem myself..and i fixed it by merging the 2 db when i updated the app
edit2: i uploaded BWDB final project ( you have your wrapper there and project to see how it works)
You can use following code to get the database created at in Documents folder. Just pass a path in documents folder and the function will copy the sqlite database in Documents folder at the given path if required. You can then use this path to create and query database tables.
+ (NSString*) createDatabaseIfRequiredAtPath:(NSString*)databasePath {
if (databasePath == nil)
return nil;
NSString *path = [NSString stringWithFormat:#"%#/%#", databasePath, kMainDBName];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
if ([fileManager fileExistsAtPath:path] == NO)
{
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[NSBundle mainBundle] pathForResource:kMainDBName
ofType:nil];
BOOL success = [fileManager copyItemAtPath:defaultDBPath
toPath:path
error:&error];
if (!success)
{
NSCAssert1(0, #"Failed to create writable database file with message '%#'.", [ error localizedDescription]);
return nil;
}
}
return path;
Is there a specific reason you'd like to use SQLLite directly, as opposed to using CoreData? CoreData uses an SQLLite database but is itself a higher-level API, and particularly with table views and such, you get a lot of functionality and template methods that are already set up for it in Xcode. Defining data models is trivial, you get tons of boilerplate code, and it's all optimized.
http://www.raywenderlich.com/934/core-data-on-ios-5-tutorial-getting-started
CoreData is sometimes described as having a steep learning curve. I disagree. If you're considering writing SQL yourself in your app, you're not going to have any trouble with CoreData.
The term "sandbox" is an abstract term for the portion of the device's filesystem that your application has read/write access to. The "Documents" directory is a specific directory within your application's sandbox. There are other files in your sandbox than just the documents directory, but most applications that save data to the filesystem in iOS do so in the documents directory.
you can open terminal and cd to /Users/Matt**/Library/Application Support/iPhone Simulator/5.1/Applications/5DB7A218-A0F6-485F-B366-91FD2F9BC062/Documents/
then sqlite3 tblStore.sqlite
then use .schema tblStore should show your table schema and you can see if it was built correctly.
Your database needs to be in the documents directory for write access. If your database was only going to be read, never written to it could be in your application bundle. One way to accomplishing this is to create your database.sqlite file and add it to the bundle and copy it to the documents directory (if it doesn't already exist there) on launch.
maybe you forgot Table Insertions. Created Database and Table but its empty table .
you trying to read the records from Table which is empty.
I tried the above didn't work for me, mainly because the example given would usually point to the DB file but not really create it.
Upon observing how sqlite3 works using the command shell, here is how I resolved the issue:
(For testing purposes), to create a file using the Linux shell, do:
sqlite3 Heider.db
This created a new database filename (Heider.db), also starts the DB command prompt to create SQL queries, ... before doing any query, just exist without doing anything by entering:
.exit
In the Linux shell check the file created, do an:
ls -ltra
You should see there is a "Heider.db" file created with 0 bytes in size, this means that the sqllite3 tool is creating a blank file with no contents.
Now, to check whether this will work, go back into sqlite:
sqlite3 Heider.db
Now this time, create a new table using:
create table test(id int not null primary key);
Insert some stuff:
insert into test(id) values (123);
Notice the query executes nicely without any errors.
Now, exit and check:
.exit
ls -ltra
The file should have some data in it, this confirms that the tool in Linux works at least...
Now, away from the Linux/Shell and back into XCODE/Development (or whatever tool you are developing with)... this makes our lives so easy as all we need to do is to create a file manually "somehow" using code instead, and then let sqlite use it.
Therefore, here is what you need to do:
// Check if the filename already exists:
DBFileName = #"MyNewFile.db";
if (![[NSFileManager defaultManager] fileExistsAtPath: DBFileName])
{
NSLog([NSString stringWithFormat:#"Database does not exists, creating: %#", DBFileName]);
[[NSFileManager defaultManager] createFileAtPath:DBFileName contents:NULL attributes:NULL];
}
// Use SQLite to access the new file to do whatever you want:
int DBOpen = sqlite3_open([_DBFileName UTF8String], &_SqlLite);
if (DBOpen == SQLITE_OK)
{
NSLog([NSString stringWithFormat: #"Client: SQLite DB Opended Successfully, creating test table..."]);
// Heider: Notice I am using "self" here because I am having this as a
wrapper class (see at the bottom of this post the whole function), you don't have to do the same, as you can use your own
SQLITE3 object directly instead...
[self ExecQuery:#"create table Test(id int not null primary key);"];
[self ExecQuery:#"insert into Test(id) values (123);"];
}
The SELF's ExecQuery I am using above is also below in case you need it:
-(BOOL)ExecQuery:(NSString *)Query {
BOOL Successful = NO;
if (sqlite3_prepare_v2(_SqlDB, [Query UTF8String], -1, &_SqlQuery, NULL) == SQLITE_OK)
{
if (sqlite3_step(_SqlQuery) == SQLITE_DONE)
{
NSLog([NSString stringWithFormat: #"Client: SQL Query Successful: %#", Query]);
} else {
NSLog([NSString stringWithFormat: #"Client-Error Executing SQL Query: %#", Query]);
}
} else {
Log(3, [NSString stringWithFormat: #"Client-Error Preparing SQL Query: %#", Query]);
}
sqlite3_finalize(_SqlQuery);
return Successful;
}
The process is to create a blank file anyhow you like, and then point sqlite3 to it and then do your SQL in the good old way... should be simple.
Good luck, I hope this helps.
H
I am having issues with the following code which loads an SQLite database.
- (NSArray *)getDatabase {
NSLog(#"Get Database Called");
NSMutableArray *retval = [[[NSMutableArray alloc] init] autorelease];
NSString *query = #"SELECT Description, UniqueID, HexCoords, NoHexCoords, NoAnnots, AnnotText, DescriptionFormatting, HexCoordsPortrait FROM MainTable";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
char *nameChars = (char *) sqlite3_column_text(statement, 0);
char *desChars = (char *) sqlite3_column_text(statement, 1);
int uniqueID = sqlite3_column_int(statement, 2);
From using breakpoints I can see that the problem is with the if statement and that the code never gets past this if statement. Can anyone spot what might be wrong ? The code was working a few months ago and I have recently upgraded to xCode 4.3 so might this be the problem ?
Thank in advance.
Yeah i agree With Joachim. there is a problem sometimes the DB doesnot really connect. what i do is a couple of things. First i add following Code in my Application App Delegate.
- (void) copyDatabaseIfNeeded {
//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [self getDBPath];
BOOL success = [fileManager fileExistsAtPath:dbPath];
if(success) {
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MyDB.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Call This Function in ApplicationDidFinishLaunching.
Now remove the data base that is in ur bundle currently. (MAKE SURE U HAD BACKUP OF IT). And (if Possible Delete All the project From Iphone Simulator Folder) Coz sometimes the previous Database is attached.
Clean Your project, Add the Data Base in ur bundle. Compile it..
Let Me know if it worked
the Get Path Function
- (NSString *) getDBPath {
//Search for standard documents using NSSearchPathForDirectoriesInDomains
//First Param = Searching the documents directory
//Second Param = Searching the Users directory and not the System
//Expand any tildes and identify home directories.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"MYDB.sqlite"];
}
I'm making a iphone app for a final project at school and I'm having some trouble. I have it connected to a sqlite database, but even though I'm not getting any errors in xcode and when I've stepped through the code with the debugger it indicates that it is working - but its not actually updating. I saw a few other threads with similar problems on the net but I was unable to solve the problem by looking at them. Here is the relevant code but if you think you need to see more I'd be happy to post it all:
first the createAndCheckCode method is called followed by the addRecipe method. I just through in a generic string until I can figure out how to get it working. i can view the database contents from a different program.
(void) addToDatabase
{
databaseName = #"recipeappdatabase.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// 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)
{
// 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];
}
// Setup the database object
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "insert into Recipes (title) VALUES ('toor')";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_text(compiledStatement, 1, sqlStatement, -1, SQLITE_TRANSIENT);
}
//if (sqlite3_step(compiledStatement) != SQLITE_DONE)
//NSAssert1(0, #"Error updating table: %s", errorMsg);
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Edit/Delete Message
I've got the following iphone code, which seems to be failing:
sqlite3_stmt *dbps;
NSString *sql = #"delete from days where day=?1;insert into days(disabled,recipe_id,day) values(?2,?3,?1)";
int rc = sqlite3_prepare_v2(db, sql.UTF8String, -1, &dbps, NULL);
...
The 'rc' return code is 1, meaning SQLITE_ERROR (SQL error or missing database, according to the sqlite site). Not sure what i've done wrong? The database 'db' is indeed open, and other queries seem to work fine.
Thanks a lot guys
Remove the insert statement from your string. It is not compiled anyway since sqlite3_prepare_v2 will "only compile the first statement in zSql."
Perhaps you should use a trigger to do your (optional) delete, or use insert or replace.
Are you sure you have copied the database in Documents directory before opening it? iPhone OS only allow write permissions in documents directory. Here is the code for copying database to Documents directory -
//function to copy database in Documents dir.
-(void) checkAndCreateDatabase{
// 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];
}
// open the database and fire the delete query...
sqlite3 *database;
NSString *sqlStatement = #"";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"%#",databasePath);
[serlf checkAndCreateDatabase];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
// here you can fire the delete query...
}
Silly me, i just had an old copy of the schema in my Documents folder, which didn't have the 'days' table in it. So i followed the instructions here: Cleaning up the iPhone simulator, and then it copied the new schema over, and it started working again.
Thanks for the help guys.