sqlite connection with Objective C - objective-c

I have a simple piece of code where I connect my sqliteDb.
My sqlite3_prepare_v2 though repeatedly fails. I narrowed it down to the following piece of code:
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"xyz" ofType:#"sqlite3"];
sqLiteDb returns null. I don't know why - tried everything I could and followed many threads.
Really struggling here - please help.

Are you sure your xyz.sqlite3 file is included in the correct Target Membership? If it is not included, then it will not be copied to your bundle when building.

Try this
in.h file
NSString *databasePath;
in .m file
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:#"dbname.sqlite"];
[self checkAndCreateDatabase];
-(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];
}

Related

sqlite3 database strangely disappears from NSDocumentDirectory

Problem: After long time running with no issues, my database is giving me a headache, it just wont stay at its place in the NSDocumentDirectory. The Database strangely disappears.
I never clear the documents-folder or delete anything. It only contains the database and saves some images in there which get downloaded if the user wants to keep them.
Has anybody an idea what could be going on?
after 3 days of struggling with this problem I can't come up with a possible solution, so please help me! :(
in my Database-Singleton I have the following init-Method
- (id)initWithName{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:kDatabaseName];
//kDatabaseName = nameOfDatabase.db
[self checkAndCreateDatabase];
return self;
}
and the checkAndCreateDatabase - method:
- (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
NSURL *urlpath = [NSURL fileURLWithPath:databasePath];
NSError *error = nil;
[urlpath setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success && sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"database opened");
sqlite3_close(database);
return;
}
else{
sqlite3_close(database);
NSLog(#"Database was created");
}
// 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];
//NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:databaseName ofType:#"sqlite3"];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
if ([self createTables]){ //creates tables of DB, works fine
NSLog(#"Tables were created");
} else {
NSLog(#"Database failed to create and open");
}
}
This code worked for a year straight. and suddenly when i needed to do some updates, the database was not saved anymore.
After a lot of troubleshooting I found out the database is being created in the Documents folder, but when i try to access the exact same path (cause i don't touch the variables) it disappears, with it's tables.
I tried different versions of my repository, all of them seem to have the problem. I really am getting mad. :(
Are you persisting the databasePath between app launches? In iOS 8 the DocumentsDirectory (and all the others, Caches, tmp, etc) became dynamic - - their name changes in between every app launch. So if you're storing the absolute path anywhere in your app it will be invalid the next time the app launches. If this is the case, a good way to fix it is to store the path relative to the DocumentsDirectory and whenever you need it just call
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
and append your path to that.
Help this helps.

Merge old Plist with new Plist

I have add new entities to existing Plist. New version of app crashes due to Plist inconsistencies.
How do I merge old plist data with new plist?
My Plist creation method is as follows
- (void)createEditableCopyOfFileIfNeeded:(NSString *)plistName
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:plistName];
success = [fileManager fileExistsAtPath:plistPath];
if (success)
{
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:plistName];
success = [fileManager copyItemAtPath:defaultPath toPath:plistPath error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Thanks in advance
I would include a version key in your plist to compare it with the version of the application. You can check the version of your application with the following snippet:
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"]
Then, at the startup of the application:
Read the plist, if there is no version key, it will be old version.
If old version then perform data migration to current version, adding the non-existing keys with default values. Here I would take in account different version numbers for future updates.
Add the version key with the version of your bundle as a value.
Save the plist.
You can do it this way:
Rename old plist e.g. old.plist
Read both plists from separate files
Merge information needed adding chosen information programatically to new plist
Save new plist merged

Unable to copy file from bundle to Documents directory, why does fileExistsAtPath return error?

I'm using the following snippet of code to attempt to copy a file from my application resources directory into the Documents area. I have used the below from the PocketOCR project on Github :
// Set up the tessdata path. This is included in the application bundle
// but is copied to the Documents directory on the first run.
NSString *dataPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"tessdata"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSLog(#"Datapath is %#", dataPath);
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:dataPath ]) {
NSLog(#"File didn't exist at datapath...");
// get the path to the app bundle (with the tessdata dir)
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *tessdataPath = [bundlePath stringByAppendingPathComponent:#"tessdata"];
if (tessdataPath) {
[fileManager copyItemAtPath:tessdataPath toPath:dataPath error:NULL];
}
}
This is the output I get from invoking the above :
2012-06-06 14:53:42.607 MyApp[1072:707] Datapath is
/var/mobile/Applications/9676D920-D6D1-4F86-9177-07CC3247A124/Documents/tessdata
Error opening data file
/var/mobile/Applications/9676D920-D6D1-4F86-9177-07CC3247A124/Documents/tessdata/eng.traineddata
I have the eng.traineddata file located in my xcode project like so
It appears that the Error is being reported when attempting to check if fileExistsAtPath, I wouldn't expect this to throw an error, but to either return YES or NO.
Is there an easier way to copy the eng.traineddata file across, or have I made a mistake which can be corrected here?
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dataPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"tessdata"];
NSLog(#"Datapath is %#", dataPath);
// If the expected store doesn't exist, copy the default store.
if ([fileManager fileExistsAtPath:dataPath] == NO)
{
NSString *tessdataPath = [[NSBundle mainBundle] pathForResource:#"eng" ofType:#"traineddata"];
[fileManager copyItemAtPath:tessdataPath toPath:dataPath error:&error];
}
-(NSString*) applicationDocumentsDirectory{
// Get the documents directory
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
return docsDir;
}
I hope also help this code to copy all type of file bundle to Document directory folder ..
NSString *pathsToReources = [[NSBundle mainBundle] resourcePath];
NSString *yourOriginalDatabasePath = [pathsToReources stringByAppendingPathComponent:#"lunchDB.sqlite"]; // file name u want copy
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex: 0];
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:#"lunchDB.sqlite"]; // file name & your original path (Document path)
if (![[NSFileManager defaultManager] isReadableFileAtPath: dbPath]) {
if ([[NSFileManager defaultManager] copyItemAtPath: yourOriginalDatabasePath toPath: dbPath error: NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, dbPath);
}
Thanks ..
Use the method fileExistsAtPath:isDirectory: of NSFileManager and specify that the path is a directory, because it is.
stringByAppendingPathComponent:#"tessdata"
The line above required that tessdata is a physical folder (a blue color folder), not a group (yellow folder as you are showing) in Xcode project. Create a folder in your Xcode project folder using the Finder and then in Xcode drag and drop the folder to your project and select the folder reference when asked.

Create database file if not exist

I'm trying to create database file in first application's launch. Used FMDB library. For this purpose I'm checking if database file already exist, if not I want to copy database file from project.
My code:
-(void)checkAndCreateDatabase {
NSLog(#"check and create database doing");
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dbFileName = [docDir stringByAppendingPathComponent:#"FriendsDatabase.sqlite3"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if (![fileManager fileExistsAtPath:dbFileName]) {
[fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:#"FriendsDatabase" ofType:#"sqlite3"] toPath:dbFileName error:&error];
NSLog(#"database created");
} else {
NSLog(#"fail to create database");
}
FMDatabase *_db = [[FMDatabase alloc] initWithPath: dbFileName];
if (![_db open]) {
[_db release];
NSLog(#"Could't open DB");
}
_db.logsErrors = NO;
self.db = _db;
[_db release];
}
But I have an error: 'NSInvalidArgumentException', reason: '*** -[NSFileManager copyItemAtPath:toPath:error:]: source path is nil'. What I'm doing wrong?
It looks like you don't have a file named "FriendsDatabase.sqlite3" in your main bundle. (If you had one, the source path in your call to -copyItemAtPath:toPath:error: wouldn't be nil.)
Try changing your code to look like:
NSString *path = [[NSBundle mainBundle] pathForResource:#"FriendsDatabase" ofType:#"sqlite3"];
[fileManager copyItemAtPath:path toPath:dbFileName error:&error];
Then step through the code with your debugger and check the value of path.
Note that you've specified "FriendsDatabase" (note the 's') as the name of the file in your code, but your comment calls the file "FriendDatabase". Perhaps you've just misspelled the filename in your code -- that's a good reason to use constants!
Caleb's answer is correct, but I'd add that you have to make sure that the sqlite3 file is a member of your target. In XCode, select your sqlite3 file, then open the "File Inspector" panel (right panel) under "Target Membership". Make sure your target app is checked.

can't save plist. path is not writable

I'm saving a lot of informations in a plist. This one is by standart in my mainBundle.
this is my method to load the path and the data from the plist. if the file in the "application support" folder doesn't exist, i'm copying it from the mainBundle to there.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
self.plistPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.plist",plistName]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: self.plistPath])
{
NSString *pathInBundle = [[NSBundle mainBundle] pathForResource:plistName ofType:#"plist"];
self.plist = [NSMutableDictionary dictionaryWithContentsOfFile:pathInBundle];
NSLog(#"plist doesnt exist");
}
else {
self.plist = [NSMutableDictionary dictionaryWithContentsOfFile:self.plistPath];
NSLog(#"plist exist");
}
NSLog(#"plist path: %#",self.plistPath);
if i add the following lines at the end, there's only NO the answer:
if([fileManager isWritableFileAtPath:self.plistPath]) NSLog(#"YES");
else NSLog(#"NO");
after all, i tried to save with [self.plist writeToFile:self.plistPath atomically:YES];, which is also not working.
sorry for answering so late - i had a lot of other stuff to do. back to my problem: i only get the error, when i try to add a new entry to my dictionary (plist). editing is no problem. i think the problem is, how i try to add the entry. my code looks like:
NSMutableDictionary *updateDict = [[self.plist objectForKey:#"comments"]mutableCopy];
NSMutableDictionary *tmpDict = [[[NSMutableDictionary alloc]init]autorelease];
[tmpDict setObject:comment forKey:#"comment"];
[tmpDict setObject:author forKey:#"author"];
[tmpDict setObject:car forKey:#"car"];
[tmpDict setObject:part forKey:#"part"];
[tmpDict setObject:date forKey:#"date"];
[updateDict setObject:tmpDict forKey:[NSNumber numberWithInt:[updateDict count]+1]];
[self.plist setObject:updateDict forKey:#"comments"];
if([self.plist writeToFile:self.plistPath atomically:YES]) {
return YES;
}
else {
return NO;
}
self.plist is my local copy of the file at plistPath. the structure of my plist looks like: https://img.skitch.com/20111026-tcjxp9ha4up8ggtfjy7ucgqcqe.png
hope this helps
Ok, so that's not the Documents directory and iOS doesn't have an Application Support directory created in the sandbox by default, which is why you can't write.
You can either change your method call to look-up the real documents directory:
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
Or, after you get the path to the Application Support directory, you must check to see if it exists already and if not, create it.
please go through the previous post which shows the different way to copy the plist from mainBundle. Use [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error]; method instead.
Did you find answer? if not, you need to change this line:
[updateDict setObject:tmpDict forKey:[NSNumber numberWithInt:[updateDict count]+1]];
to
[updateDict setObject:tmpDict forKey:[NSString stringWithFormat:#"%d",[updateDict count]+1]];
Key name is string, not object.