NSFileManager copyItemAtPath complains about a nonexistent file that does exist - objective-c

I am trying to copy a file using [[NSFileManager defaultManager] copyItemAtPath: toPath: error:] but it is failing with the following error:
4: The file does not exist.
The relevant code is below, and the file does exist and the path string is correct because it is created beforehand with the exact same file path string.
NSFileManager* manager = [NSFileManager defaultManager];
NSError* error;
NSString* fileName = [Sound getFileName:Title];
NSString* oldDirectory = [NSString stringWithFormat:#"%#%#/", [settings stringForKey:#"downloadFolder"], authorFolder];
NSString* oldFile = [oldDirectory stringByAppendingFormat:#"%#.mp3", fileName];
NSString* newFile = [NSString stringWithFormat:#"%#/iTunes/iTunes Media/Automatically Add to iTunes/%#.mp3", [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) objectAtIndex:0], fileName];
BOOL result = [manager copyItemAtPath:oldFile toPath:newFile error:&error];
if (!result && error)
{
NSLog(oldFile);
NSLog(#"There was an error copying the file to the iTunes directory! %#", [error localizedDescription]);
}
It's not the exact code, but all relevant code should be above. If I use [manager fileExistsAtPath:oldFile] the result is YES.
What could cause the copy to fail and say the file doesn't exist, even if it does?
UPDATE:
Issue fixed. Turns out the output folder was really Automatically Add to iTunes.localized, but I didn't notice this initially when just paging through the finder. Fixing the output path solved the issue! Thanks for the help.

If any of the directories in the path of the destination don't exist, you'll get a similar error to what you'd get if the source doesn't exist. Check what [manager fileExistsAtPath:[newFile stringByDeletingLastPathComponent] isDirectory:&isDir] returns.

You're using the API wrong. You need to look at the return value of -copyItemAtPath:toPath:error:. Only if that returns NO does that mean an error occurred.
If you're using ARC, your error variable should be nil if no error occurred (although this isn't technically guaranteed), but if you're using MRR it probably won't, because you never initialized it.

Related

Objective C, Copy resource file from main bundle to documents: Error Domain=NSCocoaErrorDomain Code=4 The file doesn’t exist."

Here I want to achieve a function which can copy a res file from main bundle (which is added manually from Mac Finder to Xcode project ) (Fig 1) to Document Folder.
Click here to see the Xcode project structure
And to achieve that goal, I use NSFileManager. Here is the function code:
- (void)addCamConfig {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *destPath = [documentsDirectory stringByAppendingPathComponent:CAM_CONFIG];
if (![fileManager fileExistsAtPath:destPath]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"cam" ofType:#"yaml"];
BOOL ret = [fileManager copyItemAtPath:sourcePath toPath:destPath error:&error];
if (error) {
NSLog(#"cam_ret:%d\nsourcePath:%#\ndestPath:%#\nerror:%#",ret,sourcePath,destPath,error.description);
}
}
}
In that case, sourcePath and destPath are both ensured not to be nil.
But strange thing happened in the first time, "copyItemAtPath:sourcePath toPath:destPath error:&error" return "NO" and error log showed below:
cam_ret:0
sourcePath:/private/var/containers/Bundle/Application/3DF49723-E11D-4D67-AD0F-39C2B82B80A4/NitroDemo.app/cam.yaml
destPath:/var/mobile/Containers/Data/Application/181F8B72-402B-4D0C-91D5-0D9759BC607E/Documents/nitro/cam.yaml
error:Error Domain=NSCocoaErrorDomain Code=4 "The file “cam.yaml” doesn’t exist." UserInfo={NSSourceFilePathErrorKey=/private/var/containers/Bundle/Application/3DF49723-E11D-4D67-AD0F-39C2B82B80A4/NitroDemo.app/cam.yaml, NSUserStringVariant=(
), NSDestinationFilePath=/var/mobile/Containers/Data/Application/181F8B72-402B-4D0C-91D5-0D9759BC607E/Documents/nitro/cam.yaml, NSFilePath=/private/var/containers/Bundle/Application/3DF49723-E11D-4D67-AD0F-39C2B82B80A4/NitroDemo.app/cam.yaml, NSUnderlyingError=0x2826476f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Surprisingly, when I run project second time, the error disappeared, "copyItemAtPath:sourcePath toPath:destPath error:&error" return "YES".
And if I remove this app from my iPhone, then rebuild the project , reinstall and run the app first time, the same error message appears again. And as I expected, when I run the project second time, the error disappeared.
So I wonder what exactly happened in the first time and second time?
====================
UPDATE: I solved this problem by creating an intermediate folder nitro.
The key point here is that, as you may not noticed in the log, the dest path XXX/Documents/nitro/cam.yaml contains a not existed intermediate folder nitro. So in the first time when I call copyItemAtPath:sourcePath toPath:destPath error:&error, it fails and may create that folder (just for my guess). As a result, when I run second time, the copyItemAtPath:sourcePath toPath:destPath error:&error returns YES.
Ok, based on the update you could solve it then by inserting something like below.
[NSFileManager.defaultManager createDirectoryAtPath:newDir
withIntermediateDirectories:YES
attributes:nil
error:NULL];

Why can't NSFIleManager -fileExistsAtPath not find an existing file when path is correct?

I know that the iOS Simulator is found in a different directory each time it is run; with that in mind, I have this code which gives me the directory of the Core Data sqlite files:
// find current directory for saori.sqlite
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]firstObject];
NSString *sqliteFilePath = [[documentDirectory URLByAppendingPathComponent:#"Application Support/SalonBook/saori.sqlite"] absoluteString];
if ([fileManager fileExistsAtPath:sqliteFilePath])
[MagicalRecord cleanUp]; // set stack, etc to 'nil'
else {
NSLog(#"\n\n-->sqlite files not found"); // log message "unable to find sqlite files
return;
}
This is the printout of the sqliteFilePath object:
Printing description of sqliteFilePath:
file:///Users/rolfmarsh/Library/Developer/CoreSimulator/Devices/1EE69744-255A-45CD-88F1-63FEAD117B32/data/Containers/Data/Application/C8FF20F0-41E4-4F26-AB06-1F29936C2208/Library/Application%20Support/SalonBook/saori.sqlite
And this is the image of the file from Finder:
The problem is: I go to the sqliteFilePath and the saori.sqlite file is indeed there! Why is -fileExistsAtPath failing?
Because it is still a URL. A file path doesn't have a protocol, so the prefix of your path file:/// is invalid and can't be resolved. Since an invalid path doesn't contain any files, fileExistsAtPath: returns NO.
Not to worry though, instead of calling absoluteString on the URL object, you can just call path instead and it will return the path.

moveItemAtPath. No errors, but not working

Im trying to move files. Below I'm testing if the paths exist. They do, however both copyItemAtPath and moveItemAtPath don't seem to work.
NSString *testUrl = #"/Users/justinshulman/Documents/test2";
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl]) {
NSLog(#"yes");
}
NSString *testUrl2 = #"/Users/justinshulman/Documents/test1";
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl2]) {
NSLog(#"yes");
}
NSLog(#"%#",testUrl);
NSLog(#"%#",testUrl2);
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:nil];
[[NSFileManager defaultManager]moveItemAtPath:testUrl2 toPath:testUrl error:nil];
That is exactly your problem, both move and copy will not actually overwrite the destination file if it already exists. You'll have to remove it first and then copy (or move) the other file to that URL.
Try with
[[NSFileManager defaultManager] removeItemAtPath:testUrl error:nil];
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:nil];
and it should work fine.
You should also be checking for the error instead of passing nil.
NSError* error = nil;
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:&error];
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
It also returns a bool on whether the copy was successful.
Adding to #micantox answer, always read the class reference. See class reference for NSFileManager:
If a file with the same name already exists at dstPath, this method
aborts the copy attempt and returns an appropriate error.
You should pass NSError object in error filed.
[[NSFileManager defaultManager]copyItemAtPath:testUrl2 toPath:testUrl error:&error];
Error Domain=NSPOSIXErrorDomain Code=17 UserInfo=0x100457e80 "The operation couldn’t be completed.
[[NSFileManager defaultManager]moveItemAtPath:testUrl2 toPath:testUrl error:&error];
Error Domain=NSCocoaErrorDomain Code=512 UserInfo=0x1004a2270
Use replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:
Replaces the contents specified by the first URL with the contents of
the second URL in a manner that insures no data loss occurs.
#justin, First thing it never works only. Because you are trying to copy the source path to destination path where both path are same. Second thing, how NSFileManager copy or move api works is, you have to copy or move source path to different destination path with appending your appropriate path component. For example see the code below:--
NSString *testUrl = #"/Users/home/Documents/source.rtf";
//
if ([[NSFileManager defaultManager]fileExistsAtPath:testUrl]) {
NSLog(#"yes");
}
//Below destination is folder name which should be exist on your machine or else you can create programmatically as well
NSString *testUrl2 = #"/Users/home/Documents/destination";
NSLog(#"%#",testUrl);
NSLog(#"%#",testUrl2);
NSError *err=nil;
//Now we are copying the souce path to destination folder with appending file name (it can be any your name becuase file manager copy source file contents to your destination file contents)
//Here given file name is a destination.rtf where you can give any your name. Also this is for copying source contents to destination contents
NSFileManager *fm=[NSFileManager defaultManager];
if ([fm copyItemAtPath:testUrl toPath:[testUrl2 stringByAppendingPathComponent:#"destination.rtf"] error:&err])
{
NSLog(#"success");
}
else
{
NSLog(#"%#",[err localizedDescription]);
}

Create a Folder (bundle) in Cocoa

I'm trying to programmatically create a folder with Cocoa.
I've written an NSString category and we've got the following function there :
- (void)createAsFolder
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError* err = nil;
[fileManager createDirectoryAtPath:self withIntermediateDirectories:YES attributes:nil error:&err];
if (err)
{
NSLog(#"ERROR : %#",err);
}
}
So, in a few words, let's say we have an NSString* path = #"/some/path/is/here";, we can create it simply by :
[path createAsFolder];
The thing is, although it works PERFECTLY for normal folders, it does NOT when the path specified is a bundle (that is : WITH an extension). E.g.
NSString* path = #"/this/is/a/path/to/some/bundle.bun";
[path createAsFolder];
The above does NOT work.
Any ideas on how to fix that?
OK, here's the answer (thanks to #thundersteele), if you want to copy a full file tree from on place to another :
NSFileWrapper* w = [[NSFileWrapper alloc] initWithPath:initialPath];
[w writeToFile:destinationPath atomically:YES updateFilenames:YES];
And yep : it has ABSOLUTELY no problem whether the subfolders are packages/bundles or whatever. Not that hard, huh? Just 2 lines... lol
Try NSFileWrapper instead. I think it can do what you want to do.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSFileWrapper_Class/Reference/Reference.html

Failure to write NSString to file on iPad

I'm using a text file to save the changes made by a user on a list (the reason that I'm doing this is so that I can upload the text file to a PC later on, and from there insert it into an Excel spreadsheet). I have 3 data structures: A NSMutableArray of keys, and a NSMutableDictionary who's key values are MSMutableArrays of NSStrings.
I iterate through these data structures and compile a file string that looks much like this:
(Key);(value)\t(value)\t(value):\n(Key);(value).. .so on.
SO, onto the actual question: When I attempt to save it, it fails. I'm 99% sure this is because of the file path that I'm using, but I wanted backup to check this out. Code follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [paths objectAtIndex:0];
NSString *fileString = [NSString stringWithString:[self toFileString]];
if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL]){
NSLog(#"File save failed");
} else {
// do stuff
}
(Code above is re-copied, since the actual code is on a different computer. It compiles, so ignore spelling errors?)
I tried using NSError, but I got bogged down in documentation and figured I might as well ask SO while trying to figure out how to properly use NSError (might be a little bit of an idiot, sorry).
99% sure it's the NSArray *paths line that's tripping it up, but I don't know how else to get the documents directory.
Edit: Problem solved, and one final question: If I save it to the App's document directory, where can I go after I close the app to see if it saved properly? If it works like I think it does, isn't it sandboxed in with the app's installation on the simulator? (i.e. no way of checking it)
NSLog() that filePath string. I think you're trying to write to the directory itself, not to a file.
Try this instead:
filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"myfile.txt"];
What is the file name you want to save? The method
NSArray *paths = NSSearchPathForDirectoriesInDomains(...);
NSString *filePath = [paths objectAtIndex:0];
...
if(![fileString writeToFile:filePath ...
means you are saving the string into a file path which has the same name as a folder. This will of course fail. Please give it a name, e.g.
NSString* fileName = [filePath stringByAppendingPathComponent:#"file.txt"];
if(![fileString writeToFile:fileName ...
and try again.
BTW, to use NSError:
NSError* theError = nil;
if(![fileString writeToFile:fileName ... error:&theError]) {
// ^^^^^^^^^
NSLog(#"Failed with reason %#", theError);
// theError is autoreleased.
}