Xcode - Save sqlite3 database in Library folder - objective-c

I'm developing a app that use a Sqlite3 database. For now the db is inside app package, but I know that I'll have problems to update the data when I test in real world, with a real iPhone.
1 - How can I copy, at bundle phase, my project database to Library folder?
2 - How can I access this copied database? For now my code is:
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myDatabase.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
What I'll have to change?
3 - How can I preserve this database (with the user's data) if I update the app?
Thanks in advance! ;)

You have to copy the database from the Resources folder to Documents folder
sqlite3 *database;
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"Lists.db"];
success = [fileManager fileExistsAtPath:writableDBPath];
// The writable database does not exist, so copy the default to the appropriate location.
if(!success)
{
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Lists.db"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
TFLog(#"Failed moving database... %#",[error localizedDescription]);
return;
}
}
When you update your app database in your Documents folder will be their you just need to check the condition
success = [fileManager fileExistsAtPath:writableDBPath];
if it does not exits new database will be copied their.
Just keep in mind database structure is same as before.

Related

How to copy database file in Application Support directory in Cocoa without losing data?

I have tried this code but it creates BLANK database.sqlite file in document directory.
- (void)createEditableCopyOfDatabaseIfNeeded
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"prediction.sqlite"];
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:#"prediction.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
How do I ensure that when I copy database file it does not create an empty database?
Any help would be greatly appreciated.
First your code were not attempts to create a database file.
So... You just want to copy the database file “prediction.sqlite” from source bundle to UserDomain dir?
If this is your purpose,You should pay attention to the code:
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
The correct should be:
if (!success) return;
// The writable database does not exist, so copy the default to the appropriate location.
The directories returned by NSSearchPathForDirectoriesInDomains are not guaranteed to exist; you need to create them yourself, if necessary (see the documentation). NSFileManager's createDirectoryAtPath:withIntermediateDirectories:attributes:error: can help with that.#Anomie
try those code:(before you copy the "prediction.sqlite" file, you need add the file in to your project bundle.):
- (void)createEditableCopyOfDatabaseIfNeeded
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"prediction.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (!success){
// create the directories, because the directories returned by NSSearchPathForDirectoriesInDomains are not guaranteed to exist
if (![fileManager createFileAtPath:writableDBPath contents:nil attributes:nil]) {
return;
}
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"prediction.sqlite"];
// remove the same file, before you copy the file
[fileManager removeItemAtPath:writableDBPath error:nil];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}

Objective-c load and write plist - error

Hi I'm practicing with plists and I learned that there are 2 different ways to load them
FIRST METHOD:
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [path lastObject];
NSString *filePath = [documents stringByAppendingPathComponent:#"test.plist"];
self.array = [NSArray arrayWithContentsOfFile:filePath];
SECOND METHOD:
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Ingredients" ofType:#"plist"];
self.array = [NSArray arrayWithContentsOfFile:filePath];
I don't understand clearly which way it's best... but I noticed that if I use the second one, I can't write in the plist. can anyone tell me more about it? which is the best and correct way? What's the difference?
i'm doing some tests and i have some code working only with one method...
//using this code the nslog will print null
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"Ingredients.plist"];
ingredients = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"ingredients:%#", self.ingredients);
//using this code the nslog will print the content of the array
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Ingredients" ofType:#"plist"];
ingredients = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"Authors:%#", self.ingredients);
First Method
Your app only (on a non-jailbroken device) runs in a "sandboxed" environment. This means that it can only access files and directories within its own contents. For example Documents and Library.
Reference iOS Application Programming Guide.
To access the Documents directory of your applications sandbox, you can use the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
This Documents directory allows you to store files and subdirectories your app creates or may need.
To access files in the Library directory of your apps sandbox use (in place of pathsabove):
[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
Second Method
The Second Method is used to write the file in the Apps main bundle.
The main bundle is the bundle that contains the code and resources for the running application. If you are an application developer, this is the most commonly used bundle. The main bundle is also the easiest to retrieve because it does not require you to provide any information.
It is better to copy the file from App Main Bundle to App Document Directory and then use the document directories path to read/write file.
If you are using the first method you need to copy the file from your main resources to the Documents Directory.
Code to Copy file from app bundle to App's Document Directory
#define FILE_NAME #"sample.plist"
// Function to create a writable copy of the bundled file in the application Documents directory.
- (void)createCopyOfFileIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:FILE_NAME];
success = [fileManager fileExistsAtPath:filePath];
if (success){
return;
}
// The writable file does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:FILE_NAME];
success = [fileManager copyItemAtPath:defaultDBPath toPath:filePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
}
Sample Code Dropbox Link

Copy file to App Document folder automatically

I want to copy set of files from project resource folder to the app document folder automatically at the time of app first launch. Can I do this. Do I need to do it through the code.
BOOL success;
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"Data.txt"];
success = [fileManager fileExistsAtPath:filePath];
if (!success) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"txt"];
success = [fileManager copyItemAtPath:path toPath:filePath error:&error];
}
In application did finish launching with options

SQLite Step Failed: attempt to write a readonly database , using wrapper

I keep getting an error "SQLite Step Failed: attempt to write a readonly database" when using this code to copy a database:
-(void)createEditableCopyOfDatabaseIfNeeded
{
// Testing 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:#"Money.sqlite"];
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:#"Money.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath
toPath:writableDBPath
error:&error];
if(!success)
{
NSAssert1(0,#"Failed to create writable database file with Message : '%#'.",
[error localizedDescription]);
}
}
I am using the above code in AppDelegate
and this:
NSString *writableDBPath =
[[NSBundle mainBundle] pathForResource:#"Money"
ofType:#"sqlite"];
In ViewController.m
I am using http://th30z.netsons.org/2008/11/objective-c-sqlite-wrapper/ what am I doing wrong?
This is happening again and again... It was working fine before but again the problem started.
The problem is that you cannot write anything to your bundle. To be able to change your database you need to copy it to application's documents or caches folder and work with that copy.

Copying plist file to custom folder in ApplicationSupport (Objective-C)

I have this piece of code, which copies a plist file to the ApplicationSupport directory in the users folder.
NSString *resourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:kAutonumberPlist];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *dataPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:kAutonumberPlist];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dataPath]) {
[fileManager copyItemAtPath:resourcePath toPath:dataPath error:nil];
}
How ca I change it so that instead of copying the file into ~User/Library/ApplicationSupport, it will copy it into ~User/Library/ApplicationSupport/AnotherFolder. The "AnotherFolder" already exists by the way.
Thank you!
You are already using stringByAppendingPathComponent - you could just use it again.
For example:
NSString *dataPath = [[[paths objectAtIndex:0]
stringByAppendingPathComponent: #"AnotherFolder"]
stringByAppendingPathComponent: kAutonumberPlist];