How to replace a file or folder by using NSFileManager? - objective-c

For using "copyItemAtPath" method of NSFileManager, if exists the same file or folder, occur error.
Is there the replace feature in NSFileManager?
(If exists the same file, delete and copy If not, just copy.)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self generateTableContents];
}
- (void)generateTableContents {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *appsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [appsDirectory objectAtIndex:0];
[fileManager changeCurrentDirectoryPath:documentPath];
[fileManager createDirectoryAtPath:#"user_List1" withIntermediateDirectories:YES attributes:nil error:nil];
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
NSError *error; // to hold the error details if things go wrong
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"IMG_0523" ofType:#"JPG"];
if ([fileManager copyItemAtPath:sourcePath toPath: #"./user_List1/newIMG_0523.JPG" error:&error]) {
NSLog(#"Copy complete!");
} else {
NSLog(#"Copy Failed : %#", error);
}
if ([fileManager copyItemAtPath:#"./user_List1" toPath: #"./user_List2" error:&error]) {
NSLog(#"Copy complete!");
} else {
NSLog(#"Copy Failed : %#", error);
}
}
Excuted result is complete.
/Users/nice7285/Library/Caches/appCode10/DerivedData/tempPrg-dd15264d/Build/Products/Debug-iphonesimulator/tempPrg.app/tempPrg
Simulator session started with process 1184
2012-10-04 06:28:32.653 tempPrg[1184:11303] Copy complete!
2012-10-04 06:28:32.672 tempPrg[1184:11303] Copy complete!
Process finished with exit code 143
But When already exist folder.
Result is fail.
2012-10-04 06:48:31.488 tempPrg[1243:11303] Copy Failed
2012-10-04 06:48:31.497 tempPrg[1243:11303] Copy Failed

You need to do an atomic save, for example:
NSData *myData = ...; //fetched from somewhere
[myData writeToFile:targetPath atomically:YES];
But since you're using copyItemAtPath, I have no idea if atomically works with it so I would just go for the quick dirty hack:
if ([fileManager fileExistsAtPath:txtPath] == YES) {
[fileManager removeItemAtPath:txtPath error:&error]
}
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"txtFile" ofType:#"txt"];
[fileManager copyItemAtPath:resourcePath toPath:txtPath error:&error];
Sources: IOS: copy a file in documents folder

Starting with iOS 4 (and macOS 10.7) there is an explicit method for replacing one item with another. It even keeps the properties of the original file (optional)
- (BOOL)replaceItemAtURL:(NSURL *)originalItemURL withItemAtURL:(NSURL *)newItemURL backupItemName:(NSString *)backupItemName options:(NSFileManagerItemReplacementOptions)options resultingItemURL:(NSURL * _Nullable *)resultingURL error:(NSError * _Nullable *)error;
Replaces the contents of the item at the specified URL in a manner
that ensures no data loss occurs.
replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

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]);
}
}

Adding sqlite database: No such file or directory

I wan't to add a sqlite file to preload data for my app. I created this file and copied it into the project directory. I added it to Copy Bundle Resources and copy files. But when I wan't to use this file I get an error saying that there is no such file. I would appreciate if anyone could help me.
The code is listed below:
NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"CookBookData" ofType:#"sqlite"]] ;
if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&error]) {
NSLog(#"Couldn't preload data, error: %#", error);
}
The error:
2015-07-29 15:42:34.342 CookBook v1[2897:24912] Couldn't preload data, error: Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)" UserInfo=0x7fe4a35275d0 {NSSourceFilePathErrorKey=/Users/user/Library/Developer/CoreSimulator/Devices/.../data/Containers/Bundle/Application/.../CookBook v1.app/CookBookData.sqlite, NSUserStringVariant=(
Copy
), NSDestinationFilePath=/Users/user/Library/Developer/CoreSimulator/Devices/.../Documents/CookBookModel 2.sqlite, NSFilePath=/Users/user/Library/Developer/CoreSimulator/Devices/.../data/Containers/Bundle/Application/.../CookBook v1.app/CookBookData.sqlite, NSUnderlyingError=0x7fe4a35145d0 "The operation couldn’t be completed. No such file or directory"}
I tried to change my code:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"CookBookModel3.sqlite"];
NSError *error = nil;
NSString *storePath = [storeURL path];
NSLog(#"Error %#", error);
NSString *dbName = #"CookBookData.sqlite";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentDirectory stringByAppendingString:dbName];
BOOL success = [fileManager fileExistsAtPath:dbPath];
if (!success) {
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbName];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (success) {
NSLog(#"Success");
}
else{
NSLog(#"Error %#", [error localizedDescription]);
}
}
else {
NSLog(#"Already exists");
}
if (![[NSFileManager defaultManager] copyItemAtPath:dbPath toPath:storePath error:&error]) {
NSLog(#"Couldn't preload data, error: %#", error);
}
But I get another error:
2015-07-30 12:12:29.118 CookBook[29209:208802] Couldn't preload data, error: Error Domain=NSCocoaErrorDomain Code=516 "The operation couldn’t be completed. (Cocoa error 516.)" UserInfo=0x7fe9526318b0 {NSSourceFilePathErrorKey=/Users/user/Library/Developer/CoreSimulator/Devices/DA5D105A-C9AF-4C91-80B3-DFF2C157CC92/data/Containers/Data/Application/BEED2AB9-BFDA-40AC-A8D0-55DD81F9D985/Library/DocumentationCookBookData.sqlite, NSUserStringVariant=(
Copy
), NSDestinationFilePath=/Users/user/Library/Developer/CoreSimulator/Devices/DA5D105A-C9AF-4C91-80B3-DFF2C157CC92/data/Containers/Data/Application/BEED2AB9-BFDA-40AC-A8D0-55DD81F9D985/Documents/CookBookModel3.sqlite, NSFilePath=/Users/user/Library/Developer/CoreSimulator/Devices/DA5D105A-C9AF-4C91-80B3-DFF2C157CC92/data/Containers/Data/Application/BEED2AB9-BFDA-40AC-A8D0-55DD81F9D985/Library/DocumentationCookBookData.sqlite, NSUnderlyingError=0x7fe952610c30 "The operation couldn’t be completed. File exists"}
2015-07-30 12:12:29.121 CookBook[29209:208802] Managed object context
And another variant:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *path = [documentsDir stringByAppendingPathComponent:#"CookBookData.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager copyItemAtPath:path toPath:storePath error:&error];
Use the following to get the paths to the file in question:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *path = [documentsDir stringByAppendingPathComponent:#"CookBookData.sqlite"];
You can then copy the file with the following:
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database.sqlite"];
[fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
I would include more checking and validation than the above - in particular the fileExistsAtPath: method in NSFileManager.
I hope, this gonna help:
NSError *error;
NSString *dbName = #"CookBookData.sqlite";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentDirectory stringByAppendingString:dbName];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbName];
if ([fileManager fileExistsAtPath:defaultDBPath]) { //check if file exists in NSBundle
BOOL success = [fileManager fileExistsAtPath:dbPath]; // check if exists in document directory
if (!success) {
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error]; //copy to document directory
if (success) {
NSLog(#"Database successfully copied to document directory");
}
else{
NSLog(#"Error %#", [error localizedDescription]);
}
}
else {
NSLog(#"Already exists in document directory");
}
}
else{
NSLog(#"Does not exists in NSBundle");
}
Two things you can check for, go to your document directory of application, check if you can go there and other thing is make sure your sqlite file is properly added to project, you can delete and add again. Let me know, if still there is issue

Renaming an existing file with Obj-C

I've seen this question asked a few times but I have been unable thus far to achieve success using any of the post solutions. What I am trying to do is rename a file in the local storage of an app (also kind of new to Obj-c). I am able to retrieve the old path and create the new path, but what would I have to write in order to actually change the files name?
What I have thus far is:
- (void) setPDFName:(NSString*)name{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* initPath = [NSString stringWithFormat:#"%#/%#",[dirPaths objectAtIndex:0], #"newPDF.pdf"];
NSString *newPath = [[NSString stringWithFormat:#"%#/%#",
[initPath stringByDeletingLastPathComponent], name]
stringByAppendingPathExtension:[initPath pathExtension]];
}
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtPath:initPath toPath:newPath error:&error];
The code is very messy; try this:
- (BOOL)renameFileFrom:(NSString*)oldName to:(NSString *)newName
{
NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *oldPath = [documentDir stringByAppendingPathComponent:oldName];
NSString *newPath = [documentDir stringByAppendingPathComponent:newName];
NSFileManager *fileMan = [NSFileManager defaultManager];
NSError *error = nil;
if (![fileMan moveItemAtPath:oldPath toPath:newPath error:&error])
{
NSLog(#"Failed to move '%#' to '%#': %#", oldPath, newPath, [error localizedDescription]);
return NO;
}
return YES;
}
and call this using:
if (![self renameFileFrom:#"oldName.pdf" to:#"newName.pdf])
{
// Something went wrong
}
Better still, put the renameFileFrom:to: method into a utility class and make it a class method so it can be called from anywhere in your project.

Getting error while trying to make writeable copy of sqlite

I have a copy of my db in Supporting Files folder. I need update a couple of tables and I am trying to use method to make writeable copy of db.
- (void) createEditableDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *writableDB = [documentsDir stringByAppendingPathComponent:#"yc_ch.db"];
success = [fileManager fileExistsAtPath:writableDB];
NSString *defaultPath = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"yc_ch.db"];
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file:'%#'.", [error localizedDescription]);
}
}
I have created (typed, physically) the 'Documents' folder in the root. At moment hierarchy of docs looks like this:
When I run my app. I am getting NSAssert1(0, #"Failed to create writable database file:'%#'.", [error localizedDescription]);.
What's wrong with it?
You copy the file even if the file already exists in the Document, I think NSFileManager copyFileAtPath: will return NO if file already exists at destination, hence failing your check and raising your NSAssert
success = [fileManager fileExistsAtPath:writableDB];
if (success)
{
return;
}
So the file isn't copied over if it already exists in your document. If that's what causing the error, can you actually post the assertion, [error localizedDescription] will at least tell you what is wrong with it at the moment.
Edit:
- (void) createEditableDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *writableDB = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"yc_ch.db"];
NSLog(#"Document path = %#", writableDB);
success = [fileManager fileExistsAtPath:writableDB];
if (success)
{
return;
}
NSString *defaultPath = [[NSBundle mainBundle] pathForResource:#"yc_ch" ofType:#"db"];
NSLog(#"defaultPath = %#", defaultPath);
error = nil;
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file:%#", [error localizedDescription]);
}
if (error)
{
NSLog(#"error = %#", [error localizedDescription]);
}
}

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.