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.
Related
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.
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];
}
Hi new Iphone programmer here
I'm trying to connect to my sqlite database but having problems, I have the sqlite library, and the database file in my supporting files folder, it
doesn't connect when i load the simulator
(void)viewDidLoad
{
[super viewDidLoad];
NSString *docsDir;
NSArray *dirPaths;
//Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
docsDir = [dirPaths objectAtIndex:0];
//Builds the path to the database file
databasePath = [[NSString alloc]
initWithString:[docsDir stringByAppendingPathComponent:#"PetrolWatch.sqlite"]];
NSFileManager *fileMgr = [NSFileManager defaultManager];
if([fileMgr fileExistsAtPath:databasePath]== YES)
{
lblStatus.text = #"Path Exists";
}
else
{
lblStatus.text = #"Failed to open db";
}
}
Thank you in advance
You're looking for the SQLite file in your documents directory, despite the fact that resources are loaded to the application bundle, then copied over to the documents directory manually. Because I see no code that moves the file out of the bundle, perhaps [[NSBundle mainBundle]pathForResource:] would be a more fruitful way of checking for the existence of your file (nil path = no resource).
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.
I have some data in a .txt file that I dragged over to resources in xcode 4.2. I then use some methods that call upon this file, read it, and display it on the screen to the user. It works. My problem is writing to the end of the same file (aka updating the file based on something the user did) directly on the iphone/ the simulator. It does not write for I feel I am not calling upon the right location and perhaps method. This is my code to write to the end of file, if anyone knows why this is not working it would be tremendous help.
Thank you
-(void)updateFile:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//append filename to docs directory
NSString *myPath = [documentsDirectory stringByAppendingPathComponent:#"Mom.txt"];
fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:myPath];
dateCombinedString= [dateCombinedString lowercaseString];
writtenString= [[NSString alloc]initWithFormat:#", %#, %#, %#",dateString,trimmedString,ForDisplay];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[writtenString dataUsingEncoding:NSUTF8StringEncoding]];
[writtenString release]
}
The file you dragged to Xcode is inside your app resources. You can get the path to resource with this line of code:
NSURL* fileUrl = [[NSBundle mainBundle] URLForResource:#"Mom" withExtension:#"txt"];
However, you cannot modify the files in the resource directory therefore you should first copy that file to your document directory, then modify it with the code in the question.
Here is how you can copy file from resources if the file does not exist on the documents folder:
NSFileManager* fm;
fm = [NSFileManager defaultManager];
//only copy it from resources if it does not exits
if(![fm fileExistsAtPath:myPath]){
NSURL* myUrl = [NSURL fileURLWithPath:myPath];;
NSError* error = nil;
[fm copyItemAtURL:fileUrl toURL:myUrl error:&error];
//handle the error appropriately
}