Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overwrite a file with NSFileManager when copying?

I'm using this method to copy a file:

[fileManager copyItemAtPath:sourcePath toPath:targetPath error:&error];

I want to overwrite a file when it exists already. The default behavior of this method is to throw an exception/error "File Exists." when the file exists. There's no option to specify that it should overwrite.

So what would be the safest way to do this?

Would I first check if the file exists, then delete it, and then attempt to copy? This has the danger that the app or device goes OFF right in the nanosecond after the file has been deleted but the new file hasn't been copied to that place. Then there's nothing.

Maybe I would have to change the name of the new file first, then delete the old, and then re-change the name of the new? Same problem. What if in this nanosecond the app or device goes OFF and renaming doesn't happen?

like image 537
Proud Member Avatar asked May 26 '11 10:05

Proud Member


3 Answers

If you can't/don't want to keep the file contents in memory but want an atomic rewrite as noted in the other suggestions, you can first copy the original file to a temp directory to a unique path (Apple's documentation suggests using a temporary directory), then use NSFileManager's

-replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

According to the reference documentation, this method 'replaces the contents of the item at the specified URL in a manner that insures no data loss occurs.' (from reference documentation). The copying of the original to the temporary directory is needed because this method moves the original file. Here's the NSFileManager reference documentation about -replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:

like image 82
mz2 Avatar answered Nov 17 '22 23:11

mz2


You'd want to do an atomic save in this case, which would be best achieved by using NSData or NSString's writeToFile:atomically: methods (and their variants):

NSData *myData = ...; //fetched from somewhere
[myData writeToFile:targetPath atomically:YES];

Or for an NSString:

NSString *myString = ...;
NSError *err = nil;
[myString writeToFile:targetPath atomically:YES encoding:NSUTF8StringEncoding error:&err];
if(err != nil) {
  //we have an error.
}
like image 29
Jacob Relkin Avatar answered Nov 17 '22 23:11

Jacob Relkin


If you're not sure if the file exists, this works on swift 3+

try? FileManager.default.removeItem(at: item_destination)
try FileManager.default.copyItem(at: item, to: item_destination)

The first line fails and is ignored if the file doesn't already exist. If there's a exception during the second line, it throws as it should.

like image 23
Ajmal Kunnummal Avatar answered Nov 17 '22 23:11

Ajmal Kunnummal