I can't copy my sqlite database to the users documents folder, the database has always 0 byte but should have 322 kb.
I have also checked that the database is included in the target membership.
The Problem is that i can't copy the database correctly.
Here is my code:
-(void)initDatabase
{
// Create a string containing the full path to the sqlite.db inside the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"wine.sqlite"];
// Check to see if the database file already exists
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
// Create the database if it doesn't yet exists in the file system
if (!databaseAlreadyExists)
{
// Get the path to the database in the application package
NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:#"wine" ofType:#"sqlite"];
//NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"wine.sqlite"];
// Copy the database from the package to the users filesystem
[[NSFileManager defaultManager] copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
NSLog(#"Database created");
}
}
}
There is a logic error, you check for databaseAlreadyExists inside the
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK) { ... }
block.
What you probably meant is
// Check to see if the database file already exists
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
// Create the database if it doesn't yet exists in the file system
if (!databaseAlreadyExists)
{
// Get the path to the database in the application package
NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:#"wine" ofType:#"sqlite"];
// Copy the database from the package to the users filesystem
[[NSFileManager defaultManager] copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
NSLog(#"Database created");
}
// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
...
}
you open a DB (making a new 0 byte one if it isnt there using sqlite3_open and THEN copy the shipped one right where the new DB should be..
you check if DB_docs is there
but then you open DB_docs with sqlite no matter what
and THEN you try copy it from the bundle into the OPENED File path
-(void)initDatabase
{
// Create a string containing the full path to the sqlite.db inside the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"wine.sqlite"];
// Check to see if the database file already exists
BOOL databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
// Create the database if it doesn't yet exists in the file system
if (!databaseAlreadyExists)
{
// Get the path to the database in the application package
NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:#"wine" ofType:#"sqlite"];
// Copy the database from the package to the users filesystem
[[NSFileManager defaultManager] copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
NSLog(#"Database created");
}
// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
NSLog(#"db opened");
}
}
I just use the following method written in AppDelegate,
(It seems like the method body is huge, but it's actually not. Just remove all the comments and see for the clarity after you read the comments):
#pragma mark - SQLite DB
// SQLite Copy the Database.
-( void ) checkAndCreateDatabase
{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
NSString *databasePath = [LIBRARY_DIR_PATH stringByAppendingPathComponent:DATABASE_NAME];
NSLog(#"%#",databasePath);
// 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;
NSLog(#"DB WASN'T THERE! SO GONNA COPY IT!");
// 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:DATABASE_NAME];
success = [fileManager fileExistsAtPath:databasePathFromApp];
if (success) {
NSLog(#"SIMULATOR FOUND DB FROM APP, GONNA COPY IT");
} else {
NSLog(#"SIMULATOR COULDN'T FIND DB\nIT SHOULD BE IN : %#\nPlease check if you have added the DB file and selected its target", databasePathFromApp);
}
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}
And call that method within
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
As follows:
//Copy the SQLite3 DB File
[self checkAndCreateDatabase];
Hint: Keep in mind that you need to add the SQLite DB file to your correct target membership when you drag and drop the db file to the XCode Project.
Related
I'm new to the iPhone development and Mac OS, please bear with the silly query. But I tried hard to get into depth but couldn't found the solution for the problem.
I have created a database in sqlite through command prompt. The database is saved in Users/DNamto/resources.db
But when am trying to open this db in my iPhone application using the following code snippet
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc]initWithString: [docsDir stringByAppendingPathComponent:#"resources.db"]];
The database fails to open up.
The database path which the application is searching for is :
/Users/DNamto/Library/Application Support/iPhone Simulator/6.0/Applications/C82C3DAF-4E95-49A7-9A4F-4D69B056DC9D/Documents/resources.db
Can anyone help me to get the correct database path.
Can we hard code the DB path so that my application links to it. If yes then please provide the code snippet.
Add your database in the application & check if the db is present in doc directory or not, if not then you need to copy it in doc directory and then access it.
For cppy the db in doc directory use following code snippet
- (void)copyDatabaseIfNeeded {
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:[self getDBPath]];
NSString *databasePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"dbname.sqlite"];
if(success)
{
return;// remove old one.
}
[fileManager copyItemAtPath:databasePath toPath:[self getDBPath] error:nil];
}
To open the db use following code snippet
-(void)openDatabase
{
#try
{
[self copyDatabaseIfNeeded];
if(sqlite3_open([[self getDBPath] UTF8String], &mainDatabase)==SQLITE_OK)
{
NSLog(#"Database opened");
}
}
#catch (NSException *exception)
{
NSLog(#"Exception in openDatabase %# :%#",exception.name,exception.reason);
}
}
- (NSString *)getDBPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"dbname.sqlite"];
}
Use following code snippet to close the database.
-(void)closeDatabase:(sqlite3_stmt*)statement
{
#try
{
sqlite3_finalize(statement);
sqlite3_close(mainDatabase);
}
#catch (NSException *exception)
{
NSLog(#"Exception in DatabaseController closeDatabase %# :%#",exception.name,exception.reason);
}
}
You can't. In real device you can't get the hard coded path.
You need a relative path.
Here your issue is your database is not present in the document directory.
You need to add your database to your main bundle and in run time you need to check whether the db is present on document directory, if not you need to copy it to document directory using the NSFileManager.
You can use the following code to copy the database file from bundle to document directory.
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"resource.db"];
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:#"resource.db"];
NSError *error;
[[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:folderPath error:&error];
As part of my app start-up i copy bundle files to my documents directory.
This works fine for three out of four of my files but the fourth one create a Zero KB file.
running on iOS 5.0 sim. I have cleaned the build several times and checked the file name capitalization vis correct.
the file appears in the directory but is zero kb and should be 24K
any help appreciated.
-(BOOL) CheckDBs: (NSString *)dbname
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath: dbPath];
NSLog(#"AppDelegate CheckDatabase: %# = %i", dbPath, success);
if (success) {
//NSLog(#"return YES");
return YES;
}
else {
return NO;
}
} // Complete - checks if files exist in the User Documents directory
-(void) copyDBs: (NSString *) dbname
{
//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:dbname];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname];
BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (success) {
// Version 4.0 code
//NSDictionary *attribs = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
//success = [fileManager setAttributes:attribs ofItemAtPath:dbPath error:&error];
NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
}
//NSLog(#"AppDelegate copyDatase: %# = %d", dbPath, success);
if (!success) {
NSLog(#"Failed to copy database: '%#'", [error localizedDescription]);
// NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Have you also checked the original file size?
Try resetting your simulator. From the NSFileManager documentation:
If a file with the same name already exists at dstPath, this method
aborts the copy attempt and returns an appropriate error.
Make sure the destination is empty and try again. Also, check the error object.
If all that checks out there has got to be an error in spelling the file name. Check if the exact file exists in bundle, NSLog wherever you use a file name or path, etc. You should find the error. Also check the appropriate folder in the Finder.
Instead of using
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname]
try
[[NSBundle mainBundle] pathForResource:shortName ofType:#"db"]
Ok I figured out what is causing the problem.
as i run the app the appdidfinishlaunching method is not complete before one of the view controllers is loading. That view controller attempts to access one of the files being copied over from the bundle.
I'm guessing that sqlite creates the file when you attempt to access the database, it creates it with with a zero bytes length.
So when my appdidfinish launching method checks for the existance of the file it exists due to the sql call.
This is usually only going to be a problem prior to the first run of the app as after that the database will exist.
problem now is how do i get the appdidfinish launching to complete prior to the rest being allow to start as the view controller in question is part of the mainwindow.xib
I have an app leveraging Core Data SQLITE3 that works perfectly in the simulator. However i do not understand how to update the DB on the device, which i guess is the same as in app-store.
I update the DB from .txt files in the app and create the DB, this function is there only for creating the DB and will be removed in the final version. My idea is to create the DB in the simulator, lock the update part of the code and then distribute the package with an updated database.
However, when i rebuild my app on the device it still have the old data in the DB.
I have been looking around but i am afraid i do not fully understand how to solve this. I did find this thread: Can't refresh iphone sqlite3 database
I would very much appreciate if some nice person could share some light on this and help me to solve this.
Cheers
Have you copied the db file from the bundle directory (which is read only) to a writable one? (like the documents directory of each application?).
When trying to save in the device did you get a sqlite error like this?
SQLITE_READONLY 8 /* Attempt to write a readonly database */
EDIT:
All the files in the main bundle are read only, so if you need to modify one/some of them, you need to copy the files in a location that is writable. Assuming you have called the db mydb.sqlite here is some code that copies the db (only if it does not exists) to the documents directory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSString *docPath = [docDirectory stringByAppendingPathComponent:#"mydb.sqlite"];
if (![fm fileExistsAtPath:docPath]) { // file does not exists, copy it
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"mydb" ofType:#"sqlite"];
NSError *error = nil;
BOOL res = [fm copyItemAtPath:bundlePath toPath:docPath error:&error];
if (!res) {
// do something with error
}
}
Actually to use .db file inside the Bundle - it's a very bad idea.
Every thime, when I am using .db file, i am checking, if it allready exists inside my Application document directory, and then I will rewrite it.
#define DB_SHOULD_BE_REWRITTEN YES //You should update database and change allready existing db file to file from bundle
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
BOOL success = [fileManager fileExistsAtPath:writableDBPath];
if (!success || DB_SHOULD_BE_REWRITTEN)
{
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"db.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
dbPath = writableDBPath;
I'm working on a new iPhone project and I'm running into problems with sqlite. I've done this before on a different project and it worked just fine so I'm not sure exactly what is going on with this one. I'm using the same code from before, but the situation is a little different.
First of all, I'm trying this time to use Unit Testing so I've created a Cocoa Unit Test Bundle, and I got that working correctly, then I wanted to make a Unit Test for my sqlite database.
The first thing run with this test is [self checkAndCreateDatabase] which is as follows:
-(void) checkAndCreateDatabase{
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];
}
Then I attempt to open the database with the following line:
int result = sqlite3_open([databasePath UTF8String], &database);
This fails everytime with error code 14 SQLITE_CANTOPEN, and databasePath is "/Users/labuser/Library/Application Support/iPhone Simulator/Documents/projectfusion.db3".
What is odd is that when I go to that directory, Documents/ isn't there, so if I create that, then it doesn't fail, BUT projectfusion.db3 then has a size of 0kb; the tables aren't there. That makes any sqlite3_prepare_v2() fail, because the tables aren't there. If I manually copy the projectfusion.db3 file to that directory before running, then it works just fine.
Is it because I'm doing this inside the unit tests and the scripts don't have permission or something? Or is it possibly because I'm working on a school computer at my university and can't write to that directory? (I tried logging in as admin and it didn't work either).
Try the code given below,
I've set a macro called TEST so I don't have to keep commenting code out.
- (NSString *)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
#ifdef TEST
documentsDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
#endif
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:DBNAME];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) { return writableDBPath;} ;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:DBNAME];
#ifdef TEST
defaultDBPath = [[NSBundle bundleForClass:[self class]] pathForResource:#"YOURDB" ofType:#"sqlite"];
#endif
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
return (defaultDBPath);
}
Try adding the database to your application bundle in xcode and then copy it to the applications Documents directory. You can get that path using
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
I have a persistence problem in my application. I'm using sqlite database. when some insert queries executed results temporary added to the database. After restarting application the new values vanish! I think new values stored on RAM do not save on hard-disk.
-(IBAction)add:(id)sender
{
NSString *myDB;
NSString *query;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"INSERT INTO words(col_1,col_2) VALUES('asd','asd2');"];
[database executeNonQuery:query];
[database commit];
[database close];
}
-(IBAction)show:(id)sender
{
NSString *myDB;
NSString *query;
NSArray *asdasd;
NSString *asd;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"Select col_2,col_1 FROM words"];
asdasd=[database executeQuery:query];
for(NSDictionary *row in kelimeler)
{
asd=[row valueForKey:#"col_2"];
olabel1.text=asd;
}
[database commit];
[database close];
}
You need programmatically copy your database to Documents dir of application, and work with writable copy. Resources in bundle is readonly.
I use the following code to copy my database to the documents folder and than get the writable part:
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
//NSLog(writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"db.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
- (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:#"db.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
//Add initial sql statements here
} 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...
}
}
- (sqlite3*) getDB{
return database;
}
Insert the calls to your applicationDidFinishLaunching method
As far as I remember, when you use path to db the way you do, it should be added to project.
Sometimes, and especially with databases, just cleaning doesn't work!!!
Go to the dir: /Users/qjsi/Library/Application Support/iPhone Simulator/4.3/Applications
There, you'll find all the projects you've run. Find the folder containing the project your working on, and delete it. Then clean your project through Xcode, then run. The folder in that dir will be recreated, and so will the database.
NOTE: The database will be removed as well! If you have it saved in your bundle and copy it to an editable directory, please note that the database will be the same as the one in your bundle (so, without altered records made in the iPhone Simulator).