SQLite database always empty (FMDB) - ios

I'm doing a very classic routine that i've done multiple times, but in this project, its not working. When I want to work with my DB, I get " No such tables " errors. Which should be wrong. I checked the bundle database and its fine ; I checked the "result" db in the phone and its completly empty (no structure, and obviously no data).
Here is my DB creation routine. I call it every time I need the database.
+ (FMDatabase*)createAndCheckDataBase{
BOOL success;
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [docPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:#"database.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:databasePath];
//The database already exist in the application folder, we don't need to create it
if(success){
return [FMDatabase databaseWithPath:databasePath];
}
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.sqlite"];
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
return [FMDatabase databaseWithPath:databasePath];
Then i just do FMDatabase *db = [DatabaseManager createAndCheckDatabase];and i supposedly get my db. But i don't.
I created the sqlite database using the SQLite Manager plugin from Firefox, created the structure there, then imported it into the bundle.
Note : success always return true.
Any help is most welcome!

As the comments mentioned, you need to check the copy.
Here's a snippet from my code which checks whether the copy succeeded and returns the error details to the consumer
//
// notice our method to prepare the db returns bool and doesn't lose the error details
// the consumer can log, present, whatever - this is a library function
//
- (BOOL)ensureDatabasePrepared: (NSError **)error
{
// <snip bunch of code>
// copy db from template to library
if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
{
NSLog(#"db not exists");
// notice how we pass in the error ref passed to this function
if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:error])
{
return NO;
}
NSLog(#"copied");
}

Related

File missing in iOS

I have an iOS app which locates its database in what I believe is a fairly standard manner, viz:
+ (NSString *)dbPath
{
NSArray *a =
NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
NSString *dir = [a objectAtIndex:0];
return [dir stringByAppendingPathComponent:[self dbFile]];
}
Here dbFile is a function that just returns the name of the database file.
Recently users have complained that the app sometimes fails to find its database. Is there anything I am doing wrong? I have not been keeping abreast of the latest changes in iOS.
I also save db in caches in current project.Whenever user run the app,it finds db correctly.Mainly I use with FMDB.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *documentsDirectory = [documentsDir stringByAppendingPathComponent:strFileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:documentsDirectory])
{
NSString *backupDbPath = [[NSBundle mainBundle] pathForResource:#"DBName" ofType:#"sqlite"];
NSLog(#"Test backuppath %#",backupDbPath);
if (backupDbPath == nil)
{
NSLog(#"Database path is nil");
}
else
{
BOOL copiedBackupDb = [[NSFileManager defaultManager] copyItemAtPath:backupDbPath toPath:documentsDirectory error:nil];
if (!copiedBackupDb) NSLog(#"Copying database failed");
}
}
Now I remember.
An app store reviewer told me that I could not store the database in the directory that I originally intended to use, because its contents were downloaded from the internet. He was wrong of course, but I was having a lot of trouble with them at the time, so I just moved the database to the cache area as requested.
That is why is sometimes gets deleted.

SQLite file in project catalogue, use fmdb open it failed

I create a table in SQLite Manager and export it to desktop , its name is db_fo_dic.sqlite, and I can open the db_fo_dic.sqlite use SQLite Manager, so I copy the db_fo_dic.sqlite to my Xcode project catalogue, I try to open the sqlite file by FMDB, but it failed:
NSString *path = [[NSBundle mainBundle] pathForResource:#"db_fo_dic" ofType:#"sqlite"];
FMDatabase *db = [FMDatabase databaseWithPath:path];
The result info is :
2016-08-01 19:22:34.955 test_sqlite[15380:425242] The FMDatabase <FMDatabase: 0x7ff589c38f10> is not open.
This is the location path of my sqlite:
/Users/youpude/Library/Developer/CoreSimulator/Devices/87D0245A-47F7-4741-81B2-209625CB3C5E/data/Containers/Bundle/Application/47B3AD1F-C08E-4E34-8858-494A95FB9EA5/test_sqlite.app/db_fo_dic.sqlite
8 hours go through, I find the method to solve my problem finally.
#import "FMDatabaseQueue.h"
static FMDatabaseQueue *_queue;
in - (void)viewDidLoad:
_queue = [FMDatabaseQueue databaseQueueWithPath:path];
and I can query my database:
[_queue inDatabase:^(FMDatabase *db) {
FMResultSet *result = [db executeQuery:#"select * from dic_content limit 10;"];
while ([result next]) {
NSString *str = [result stringForColumn:#"tit"];
NSLog(#"%#",str);
}
}];
I use this method to replace the previous method.
You don't have write permission for content stored in app bundle. So you have to first copy your database to document directory. For more details about permissions of file system in iOS refer Apple documentation for file system.
So you can do something like,
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
self.documentsDirectory = [paths objectAtIndex:0];
self.databaseFilename = dbFilename;
NSString* destinationPath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
if (![[NSFileManager defaultManager]fileExistsAtPath:destinationPath]) {
NSString *sourcePath = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:self.databaseFilename];
NSError *error;
[[NSFileManager defaultManager]copyItemAtPath:sourcePath toPath:destinationPath error:&error];
if (error != nil) {
NSLog(#"%#",[error localizedDescription]);
}
}
here dbFilename is your database's name (in your case : db_fo_dic.sqlite).
After doing this, try to open database from destination path and it will work i think!

FMDB Database not working on device, but it is working in Simulator

I have this database that i created, i want to load it using FMDB, but on the simulator i can query select the data, but not on the ipad.
i use this code to check it.
- (void)createDatabase{
//set reference to database here
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Database filename can have extension db/sqlite.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appDBPath = [documentsDirectory stringByAppendingPathComponent:#"product.db"];
success = [fileManager fileExistsAtPath:appDBPath];
if (success) {
[self getAllData];
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"product.db"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:appDBPath error:&error];
NSAssert(success, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
[self getAllData];
}
//method used to create database and check if it exists
- (void)getAllData {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"product.db"];
database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *sqlSelectQuery = #"SELECT * FROM Categories";
// Query result
FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
while([resultsWithNameLocation next]) {
NSString *strID = [NSString stringWithFormat:#"%d",[resultsWithNameLocation intForColumn:#"id"]];
NSString *strName = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"cat"]];
NSString *strLoc = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"Location"]];
// loading your data into the array, dictionaries.
NSLog(#"ID = %#, Name = %#, Location = %#",strID, strName, strLoc);
}
[database close];
}
i put the product.db in the supporting files, and i copied it in the Copy Bundle Resources for the targets. Does anyone what i am doing wrong? i am kind of at a dead end here.
Thanks in advance
The problem occurs at the selecting stage, when i select it it returns:
"DB Error: 1 "no such table"
If you ever ran the app on the device before you refined the file open/copy logic, you may have accidentally created a blank database in the documents folder (because if you open a database and it's not found, it creates a blank database). Try deleting the app (which will remove any blank database that might be lingering about) and reinstalling the app and see if that fixes it.
By the way, in the future, you can avoid this problem by using openWithFlags (rather than open) with SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE), and that will ensure that the app never creates a blank database if it doesn't find one.
You can see the SQLite documentation on sqlite3_open (which FMDB open uses) and sqlite_open_v2 (which FMDB openWithFlags uses) for a discussion of these flags.

How to check to see if same exact file exists

I am currently using this code to copy my SQLite database, however it is currently only checking to see if the file exists... I want to change it to check if the file isn't exactly the same, for example I am worried if a database gets corrupt or doesn't copy all the way, the app will lose functionality and the only way to fix this would be to delete the App and redownload it.
So how can I compare if two files are exactly equal?
- (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];
//NSLog(#"%d",success);
if(!success) {
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database01.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
- (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];
//NSLog(#"dbpath : %#",documentsDir);
return [documentsDir stringByAppendingPathComponent:#"database01.sqlite"];
}
You can use contentsEqualAtPath:andPath: method of NSFileManager for this purpose.
Use your code something like this:
......
if(!success) {
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database01.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
success = [fileManager contentsEqualAtPath:defaultDBPath andPath:dbPath]; //verify if file size and content matches
if(!success) {
//report error
}
}
.......
And it should do the trick for you.
Edit - Forget this answer - use the one by Ayan.
Start by comparing the file sizes. If the sizes are different you know the files are not the same. This is a simple and quick check.
If the sizes are the same then you need to compare the files, byte by byte. An inefficient way would be to load both files into NSData objects and see if they are equal. This only works if the files will always be small enough to fit in memory.
A better approach is to open both files as streams and read them in chunks. Compare each chunk (say 2k each) until two chunks are different or you get to the end.

Select data from SQLite

Here is my code, and error is visible also:
In SQLite Manager from Mozzila Firefox - all queries work fine which means that DB is correct
Maybe someone can piont me out what is wrong with my code?
EDIT:
My code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"stories_db.sqlite"];
FMDatabase *db = [FMDatabase databaseWithPath:path];
//[db open];
if ([db open]==true)
{
FMResultSet *resultsFavorite = [db executeQuery:#"SELECT count(*) from favorites"];
while ([resultsFavorite next])
{
Pasaka *pasaka = [[Pasaka alloc]init];
pasaka.title = [resultsFavorite stringForColumn:#"story_name"];
pasaka.image = [resultsFavorite stringForColumn:#"image_path"];
pasaka.movie = [resultsFavorite stringForColumn:#"video_path"];
[favoriteMovieList addObject:pasaka];
}
}
EDIT 2:
When i do this query:
FMResultSet *results = [db executeQuery:#"SELECT count(*) FROM sqlite_master"];
it doesn't show any errors.
You need to copy your database to your document folder first (also check if your sqlite file was properly added in the Copy Bundle Resources in Build Phases of your project setting):
//Where your original database file is
bundleDatabasePath = [[NSBundle mainBundle] pathForResource:#"stories_db" ofType:#"sqlite"];
//Where you will copy it in order to be used
documentDatabasePath = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingString:#"/stories_db.sqlite"];
//Using the default file manager
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
//Copy empty database into document folder
if (![fileManager copyItemAtPath:bundleDatabasePath toPath:databasePath error:&error]) {
//Error
NSLog(#"Could not copy the database into document folder. Error:%#", error);
}
//Use DB
FMDatabase *db = [FMDatabase databaseWithPath:databasePath];
Reset iOS Simulator or if you are testing on device delete app,
Clean project and run again..
Just go to your document that you have got in the dbpath. seems like you your table is not present there . Just delete your app from device or simulator and manually copy the database at dbpath or copy it using NSFileManager and run your code again .
Found a solution. Moved my database file on top of the project... O.o and it worked...

Resources