Objective C SQLite Not Recognized as a Database - ios

I am attempting to create a process to backup my app data stored in a SQLite file. To this point I have been able to successfully hook into the Dropbox SDK, upload a backup file then download the backup file. I am stuck attempting to replace the current .sqlite with the downloaded backup .sqlite. Replacing the file itself did not yield desired results so I attempted to drop the tables of the current DB and re-load them from the backup. However when attempting to query the backup I am getting an error that the file is encrypted (it is not) or not a database. Error message = DB Open Error: file is encrypted or is not a database.
Code to simply count records from a table in backup below. If anyone has any ideas or even better a simpler way for me to swap out the existing .sqlite for the backup.sqlite that would be much appreciated.
self.dbManagerBackup = [[DBManager alloc] initWithDatabaseFilename:#"Backup.sqlite"];
NSString *query = [NSString stringWithFormat:#"select * from Table1"];
if (self.Backup != nil) {
self.Backup = nil;
}
self.Backup = [[NSArray alloc] initWithArray:[self.dbManagerBackup loadDataFromDB:query]];
NSLog(#"*** Backup Backup Count = %lu",(unsigned long)self.Backup.count);

Related

Sqlite File Size not decreasing after deleting some rows from database in iOS

I am programatically trying to check the size of SQLite database stored on the iphone after deleting some rows. The records get deleted, but the file size remains the same. Is there any way I can get the correct size of file after deleting records from sqlite in iOS? Please see the code below:
NSString* databasePath = [[NSString alloc]
initWithString: [self.documentsDirectory stringByAppendingPathComponent: self.databaseFilename]];
unsigned long long fileSize = [[[NSFileManager defaultManager]
attributesOfItemAtPath:databasePath error:nil] fileSize];
Thanks!
I cant use the vaccum approach because it deletes all the records . I am deleting 50% of data only, and not all. So this is not duplicate of this question
change sqlite file size after "DELETE FROM table"
The size of a SQLite database file does not necessarily shrink when records are deleted. If auto_vacuum is not enabled, it will never shrink unless you perform a vacuum operation on it.
See https://sqlite.org/lang_vacuum.html and https://sqlite.org/pragma.html#pragma_auto_vacuum .

Back Up & Receive Core data SQlite3 database

I want to back up and retrieve sqlite3 database in core data. This is to provide a quick save and restore of users data saved in an app. The plan is to email the database, then open it on the receiving device and all the previously data will magically appear and all is good.
I have read many posts and documentation on the subject but putting it all together is where I could do with some advice.
What I have done so far:
I have managed to email the sqlite3database using the following
NSString *filePath = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"db.sqlite"];
NSError *error = nil;
NSData *fileData = [[NSData alloc] initWithContentsOfFile:filePath options:0UL error:&error];
if (error != nil) {
DebugLog(#"Failed to read the file: %#", [error localizedDescription]);
} else {
if (fileData == nil) {
DebugLog(#"File data is nil");
}
}
MFMailComposeViewController *mailView = [[MFMailComposeViewController alloc] init];
if (mailView != nil) {
mailView.mailComposeDelegate = self;
[mailView setSubject:#"back up database"];
[mailView addAttachmentData:fileData
mimeType:#"application/octet-stream"
fileName:#"db.sqlite3"];
[mailView setMessageBody:#"Database attached"
isHTML:NO];
[self presentViewController:mailView animated:YES completion: NULL];
}
Further research leads me to believe I have to attach other journal files to this to this? iOS: How can I create a backup copy of my core data base? And how to export/import that copy?
Ive logged these documents as db.sqlite db.sqlite-shm db.sqlite-wal
My question is
1. Do I send all these db.sqlite db.sqlite-shm db.sqlite-wal as attachments in the email?
2. What is the procedure to receive these files and copy them into the app.
Simply opening the attachment does not work as it can only be read by certain apps. I then made a Document type in my info.plist and my app appears in the action sheet as an app to receive the document but crashes the app with error incomprehensible archive
So in summary, how can I save and restore my core data
Archive all 3 sqlite files into one zip file and name it Backup.myappbackup or something similar (it’s good to add date of backup to filename). Than you can register extension to be recognized by your application which will allow Mail.app to recognize this extension and open this backup within your app. Next you will unarchive core data files, replace them at your locations and reconfigure core data stack. You can also allow users to put your backups through iTunes Sharing and restore it from your Documents folder.

FMDB SQLite doesn't show up in my device

Again, I need someone's help. I am following the below tutorial for SQLite connection with FMDB wrapper:
http://www.icodeblog.com/2011/11/04/simple-sqlite-database-interaction-using-fmdb/
In the above tutorial, everything (creating a database and table, inserting, deleting) is done programmatically but everything works fine in simulator but when I use SQLite Manager add-on to make any insertion on the database (that was programmatically created in iOS), it doesn't show up in my testing device. Is there something that I am missing (like copying the file to my bundle) on my side? This is really driving me crazy. I am completely new to these.. Can anyone help me with this?
PS: I know I can use Core Data but for certain reasons I have to live with SQLite for this app. Any help would be greatly appreciated.
Thank you in advance.
Make sure to (a) include the db in the bundle (see here for screen snapshot of where the "copy bundle resources" can be found); and (b) programmatically copy db from bundle to documents. You can then open it from there.
Why it works on your simulator and not the device is a little curious. I can only imagine that you're reading from some fixed path, rather than programmatically retrieving the app's Documents folder?
Update:
I'd suggest checking your various return codes and make sure everything is ok.
I've taken your code sample and have (a) checked result of creation of FMDatabase; (b) checked result of open; (c) checked the result of FMResultSet, including the display of the sqlite error message; and (d) closed the result set:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"DADatabase.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:path];
NSAssert(database, #"Unable to create FMDatabase object");
NSLog(#"%#", database);
BOOL success = [database open];
NSAssert(success, #"Unable to open database");
FMResultSet *results = [database executeQuery:#"SELECT * FROM DrugList"];
NSAssert(results, #"Unable to create result set: %#", [database lastErrorMessage]);
while([results next]) {
NSString *name = [results stringForColumn:#"Drugname"];
[arrayToLoadDiseases addObject:name];
}
[results close];
[database close];
If you're running this in your simulator, you can go ahead and inspect the simulator's bundle and Documents folders if things don't go right, just to make sure everything is where it should be. You simulator's folder is something like "~/Library/Application Support/iPhone Simulator/5.1/Applications/" (replace the 5.1 with whatever version of your simulator you are using). You might have to unhide your Library folder, if you haven't already, by running the chflags nohidden ~/Library in a Terminal command-line window. Anyway, you can open up the simulator's database using Mac OS X's sqlite3 command and make sure your table is ok.
A common mistake is to fail to programmatically copy the database properly, thus the open method will create a blank database, and executeQuery will fail saying that the table was not found.
Let me know what you find.

Unable to connect SQLite Database in iOS

I am completely new to SQLite and iOS. I am following a basic tutorial on how to use SQLite in iOS:
http://www.switchonthecode.com/tutorials/using-sqlite-on-the-iphone#comment-11617
In the above link, they have specified the database as:
sqlite3 *database;
int result = sqlite3_open("/myExampleDatabase.db", &database);
but when I use the above code with replacing my database name, I get an error as specified in the subsequent alertview.
My question here is, do I have to add the database file into my resource folder? If not, do I have to have my database file somewhere that is accessible to iOS?
I suggest using FMDB wrapper for SQLite:
https://github.com/ccgus/fmdb
If you want to open a sqlite database, you might want to:
Make sure you're including your database in your bundle.
Programmatically copy the database from your bundle to your documents (esp important if user will be modifying the database; if you're only reading, you could go ahead an just open the version in the bundle).
If you're running this in your simulator, you can go ahead and inspect the bundle and Documents folders if things don't go right, just to make sure everything is where it should be. You simulator's folder is something like "~/Library/Application Support/iPhone Simulator/5.1/Applications/" (replace the 5.1 with whatever version of your simulator you are using). You might have to unhide your Library folder, if you haven't already, by running the chflags nohidden ~/Library in a Terminal command-line window.
So, the code for getting the path of the database (and copying it to the Documents if it's not there yet), might look like:
NSString *databaseName = kDatabaseName; // obviously, replace this with your database filename, e.g., #"myExampleDatabase.db"
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databaseFullDocumentPath = [documentsFolder stringByAppendingPathComponent:databaseName];
NSString *databaseFullBundlePath = [[NSBundle mainBundle] pathForResource:databaseName ofType:#""];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:databaseFullDocumentPath])
{
NSAssert([fileManager fileExistsAtPath:databaseFullBundlePath], #"Database not found in bundle");
NSError *error;
if (![fileManager copyItemAtPath:databaseFullBundlePath toPath:databaseFullDocumentPath error:&error])
NSLog(#"Unable to copy database from '%#' to '%#': error = %#", databaseFullBundlePath, databaseFullDocumentPath, error);
}
Then, if you're doing your own sqlite calls, it would be something like:
sqlite3 *database;
if (sqlite3_open_v2([databaseFullDocumentPath UTF8String], &database, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK)
{
// do whatever you want to do
}
Or, alternatively, if you're using FMDB, it would be something like:
FMDatabase *db = [[FMDatabase alloc] initWithPath:databaseFullDocumentPath];
NSAssert(db, #"Unable to open create FMDatabase");
BOOL success = [db open];
NSAssert(success, #"Unable to open database");
if (success)
{
// do whatever you want to do
}
I fully support the previous answer in most cases, however:
Are you sure you have to use sqlite3 instead of Core Data?
There are several discussion where you can get information when to use a database wrapper (like fmdb) and when to use Core Data. (Speaking personally, I love to use fmdb, but it always results in more code, complexity and most of the time a worse performance)
Core Data vs SQLite 3
Use CoreData or SQLite on iPhone?
Core Data vs Sqlite and performance
Core Data vs SQLite 3
is it worth using core data for a simple sqlite app on the iphone with one table and no relationships or complicated subtable/views?
Core Data vs. SQLite for SQL experienced developers
Some links to get started with Core Data:
Core Data Programming Guide (Apple)
Core Data Tutorial for iOS (Apple)

Using Sqlite3 VACUUM command on Core Data SQLite Persistent store

In our app, we are implementing sharing of partial Core Data SQLite database through network/email. In order to keep the file size small, I have implemented the below method to shrink the Core Data database.
- (void) shrinkDB
{
sqlite3 * database;
NSString * string = [shareStoreURL path];
const char * filename = [string cStringUsingEncoding:[NSString defaultCStringEncoding]];
char *errMsg;
if (sqlite3_open(filename, &database) == SQLITE_OK)
{
NSLog(#"Shrinking...");
if (sqlite3_exec(database, "VACUUM;", NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed execute VACUUM");
}
sqlite3_close(database);
}
}
QUESTION: The above code does shrink the database. But Apple says the implementation details of Core Data are subject to change any time. Do you think I would be safe using this method for foreseeable future? Or is there any other better solution?
The proper way to do this is by giving the NSSQLiteManualVacuumOption to the persistent store coordinator.
Snippet from documentation:
NSSQLiteManualVacuumOption
Option key to rebuild the store file,
forcing a database wide defragmentation when the store is added to the
coordinator. This invokes SQLite's VACUUM command. It is ignored by
stores other than the SQLite store. Available in OS X v10.6 and later.
Declared in NSPersistentStoreCoordinator.h.
See this: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSPersistentStoreCoordinator_Class/NSPersistentStoreCoordinator.html
How Apple structures persistent data in an SQLite database is an implementation detail which is subject to change. However, the method by which SQLite manages deleted records is independent of Apple's implementation.
That being said, the process of vacuuming a SQLite database results in rebuilding the entire database, which may have negative effects if the sqlite file is in use by a CoreData NSPersistentStoreCoordinator.
In your case, it sounds like you want to vacuum after saving changes but before sending it via email. Using the NSSQLiteManualVacuumOption option appears to only vacuum the DB when the SQLite file is initially opened.
I'd either run the above code after the file is no longer associated with a NSPersistentStoreCoordinator or use the NSSQLiteManualVacuumOption then re-open and close the file before sending it via email.
Another option is to use an external SQLite tool, such as Base on OS X, to manually vacuum files. I've also used the Firefox SQLite manager extension in the past.

Resources