I am trying to copy an mp3 file from my Resources folder to a folder inside "Documents" folder of the app. On the simulator this works fine. But when I run it on a device, copying the files gives me this error
Operation could not be completed. (Cocoa error 513.)
The source and destinations paths are fine but I still cannot copy the file. Any ideas? Where can I find out what the cocoa error code 513 means?
Thanks.
Here's the relevant source code
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:#"Files"];
NSString *insPath = [NSString stringWithFormat:#"%#.mp3", fileName];
NSString *srcPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:insPath];
NSString *destPath = [folderPath stringByAppendingPathComponent:insPath];
NSLog(#"Src: %#, Dest: %#", srcPath, destPath);
NSError *err;
[fileManager moveItemAtPath:srcPath toPath:destPath error:&err];
NSLog(#"Err desc-%#", [err localizedDescription]);
NSLog(#"Err reason-%#", [err localizedFailureReason]);
Before making a call to moveItemAtPath, I am also creating the directory "Files" and it returns a YES.
Here's the log results
Src: /var/mobile/Applications/512D7565-7EF7-4C13-A015-19EEC3F3B465/MyApp.app/MyFile.mp3, Dest: /var/mobile/Applications/512D7565-7EF7-4C13-A015-19EEC3F3B465/Documents/Files/MyFile.mp3
Err desc-Operation could not be completed. (Cocoa error 513.)
Err reason-(null)
A question
Is there a limit to the file size when copying data from resources to Documents folder? The file I am trying to copy is about 5MB. Could that be a reason?
EDIT:
Just figured out an even simpler solution. Instead of
moveItemAtPath:toPath:error:, just use copyItemAtPath:toPath:error: ... since we truly want to copy the file from the mainBundle and not move it. I should have thought of that sooner!
E.g.
[[NSFileManager defaultManager] copyItemAtPath:mainBundleFilePath
toPath:destPath
error:&err]
See my previous comments below about why this works.
I believe I have the answer to this question. I can say for sure that the issue is not the destination file path.
I was getting the same Cocoa error 513 (NSFileWriteNoPermissionError) with pretty much the exact same code:
[[NSFileManager defaultManager] moveItemAtPath:mainBundleFilePath
toPath:destPath
error:&err]
The problem appears to be that the file, coming from the mainBundle, doesn't have suitable permissions to be moved to another place. I'm not sure if this command, if executed, would actually move the file from the mainBundle or just copy it...but either way, the file manager doesn't seem to like the idea.
The solution is simple: just read the mainBundle file into an NSData object and then write the NSData to a new file. Note the destination file path is the same in both examples, which shows us that lostInTransit is correct in saying that his file path is OK.
For this example, then, the following code will work and not throw an error:
NSData *mainBundleFile = [NSData dataWithContentsOfFile:mainBundleFilePath];
[[NSFileManager defaultManager] createFileAtPath:destPath
contents:mainBundleFile
attributes:nil];
BTW, in my own code, instead of passing a nil for attributes:, I set up an NSDictionary with a NSFileModificationDate attribute. I also wrapped the createFileAtPath:contents:attributes in an error handing if-statement. In other words,
if (![[NSFileManager defaultManager] createFileAtPath:destPath
contents:mainBundleFile
attributes:myAttributes]) {
// handle error as necessary, etc...
}
It took me a while to figure all of this out, so hopefully this solution will be helpful to others.
Are you sure you are getting the path to Documents folder correctly? The absolute path in the simulator is different than the absolute path on the device.
You should use the following to make sure you get the correct path to the Documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
The path for documentsDirectory on the the device would be something like:
/var/mobile/Applications/30B51836-D2DD-43AA-BCB4-9D4DADFED6A2/Documents
The path on the simulator would be something like:
/Volumes/Stuff/Users/johnDoe/Library/Application Support/iPhone Simulator/User/Applications/118086A0-FAAF-4CD4-9A0F-CD5E8D287270/Documents
You can read more on the File & Networking page on the dev site.
That's the NSFileWriteNoPermissionError:
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html
Somehow, you do have the wrong path and it's not letting you write there. You could also delete the app and try again, in case somehow your app documents directory was set to the wrong permissions...
I'd give us the line of code doing the copy, and print outs of each variable used in that line. Then we can see what the problem is.
Related
Is there any way to save my .xml file to another directory other than "/Users/student/Library/Application Support/iPhone/Simulator/User/Applications/..."
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"accUserNames.xml"];
BOOL ok = [content writeToFile:appFile atomically:YES encoding:NSUnicodeStringEncoding error:nil];
if (!ok) {
NSLog(#"Error writing file !");
}
i wish to writeToFile: my .xml file to the desktop , any idea on how?
May the below code help,
NSString *documentsDirectory = #"/Users/student/Desktop/";
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"accUserNames.xml"];
BOOL ok = [content writeToFile:appFile atomically:YES encoding:NSUnicodeStringEncoding error:nil];
if (!ok) {
NSLog(#"Error writing file !");
}
From within the iPhone Simulator, you should be able to successfully use #"/Users/student/Desktop/accUserNames.xml" as the path to write to. However, you can't do this on an iOS device (you'll be restricted to the application's sandbox directory — it's recommended you write to the Documents folder or other folders in there, depending what type of data you're storing).
Edit: I think I understand your problem. This part of your code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
effectively finds the path to "/Users/student/Library/Application Support/iPhone/Simulator/User/Applications/..." (this is the normal place you want to save things). But if you just want to save to the desktop temporarily, you should just use appFile = #"/Users/student/Desktop/accUserNames.xml".
Note: I don't advocate this as a long-term solution, but if you just want to see the output of your program temporarily, it works fine.
If you want to upload your file to a server ("www.blahblah.com" in your example), then using the write to file methods is not the correct approach. This is only for writing data to the local file system (or on a network share, but that doesn't apply to iPhones).
If you want to transfer data to a webserver, you will need to have something on the server that will listen for a connection request, then it will need to accept the data which is transferred from your app. You cannot just write a file to "www.blahblah.com" as you would to a file system
Probably not with code, but you could try this: Open "Automator" (in the Utilities folder) and chose "Folder Action". As Input folder, you specify the directory of the documents (/Users/student/Library/Application Support/iPhone/Simulator/User/Applications/...) and then you select, from "Files & Folders", "Duplicate Finder Items" and "Move Finder Items" and select the Desktop. Hit "Save", give it a name, and all files in the documents folder should be copied to the desktop.
I have been reading alot on iPhone read/writing and I believe that everything I have is correct but for some reason it isn't working the way it should.
code is as follows
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingFormat:#"filename.rtf"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
I went into the folder iphone simulator documents folder for the app I am making and added that file into it as a safety measure and it is still not triggering the if statement. So for some reason it is not finding it which is making all of my writing and reading impossible. I even tried using the FileManager's createFile function and didn't get any results. If I need to post more code of something let me know but I figure if all of the above is correct it should be finding the file correctly. Thanks in advance.
UPDATE: Fixed. Hilariously simple mistake. Thanks everyone.
The NSString returned by NSSearchPathForDirectoriesInDomains does not have a trailing slash, thus the path you are building is incorrect. To append the filename to the use stringByAppendingPathComponent which will handle the path separaters correctly.
To create the filePath, try:
NSString *filePath =[docsDirector stringByAppendingPathComponent:#"filename.rtf"];
Have you tried using stringByAppendingPathComponent: instead of stringByAppendingFormat:. If docsDirectory does not contain a trailing slash, you will actually be creating a file called /PATH/TO/Documentsfilename.rtf rather than /PATH/TO/Documents/filename.rtf.
this must be easy but I want to put a file in the Documents folder which is read in at start up. I have the code on how to read and have confirmed its looking in the correct directory. However the file I have ,RootList.txt when saved in the Resources folder in xcode, is stored under the Root.app folder and the Documents folder is empty. Therefore its not finding this file when I start the app.
Is there a way to ensure a file is built into the Documents directory at start up (I'm running this in the simulator).
The alternative is a plist which works fine as well but I'm just curious.
In these situations I follow this approach:
First save your RootList.txt in Resources folder in xCode.You have nothing in your Documents folder, yet.
In the beginning of your applicationDidLaunch call, do:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"RootList.txt"];
if(![fileManager fileExistsAtPath:path])
{
NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/RootList.txt"]];
[data writeToFile:path atomically:YES];
}
Now, your file is in Documents folder at startup.
I am posting this question because I had a complete answer for this written out for another post, when I found it did not apply to the original but I thought was too useful to waste. Thus I have also made this a community wiki, so that others may flesh out question and answer(s). If you find the answer useful, please vote up the question - being a community wiki I should not get points for this voting but it will help others find it
How can I get a path into which file writes are allowed on the iPhone? You can (misleadingly) write anywhere you like on the Simulator, but on the iPhone you are only allowed to write into specific locations.
There are three kinds of writable paths to consider - the first is Documents, where you store things you want to keep and make available to the user through iTunes (as of 3.2):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
Secondly, and very similar to the Documents directory, there is the Library folder, where you store configuration files and writable databases that you also want to keep around, but you don't want the user to be able to mess with through iTunes:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];
Note that even though the user cannot see files in iTunes using a device older than 3.2 (the iPad), the NSLibraryDirectory constant has been available since iPhoneOS 2.0, and so can be used for builds targeting 3.0 (or even earlier if you are still doing that). Also the user will not be able to see anything unless you flag an app as allowing users to modify documents, so if you are using Documents today you are fine as long as you change location when updating for support of user documents.
Last there is a cache directory, where you can put images that you don't care exist for the long term or not (the phone may delete them at some point):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}
Note that you have to actually create the Caches directory there, so when writing you have to check and create every time! Kind of a pain, but that's how it is.
Then when you have a writable path, you just append a file name onto it like so:
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"SomeDirectory/SomeFile.txt"];
or
NSString *filePath = [cachePath stringByAppendingPathComponent:#"SomeTmpFile.png"];
Use that path for reading or writing.
Note that you can make subdirectories in either of those writable paths, which one of the example string above is using (assuming one has been created).
If you are trying to write an image into the photo library, you cannot use file system calls to do this - instead, you have to have a UIImage in memory, and use the UIImageWriteToSavedPhotosAlbum() function call defined by UIKit. You have no control over the destination format or compression levels, and cannot attach any EXIF in this way.
Thanks to Kendall & Dave, above, and I thought this amendment was useful to bring up. When using for one-off debug code, I used this trick from Mike Ash's NSBlog to eliminate the temporary variables isDir & error, minimizing the number of lines and making the verbosity almost bearable:
NSFileHandle *dumpFileHandle = nil;
#ifdef DEBUG
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&(BOOL){0}])
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:&(NSError*){nil}];
NSString *dumpPath = [cachePath stringByAppendingPathComponent:#"dump.txt"];
[[NSFileManager defaultManager] createFileAtPath:dumpPath contents:nil attributes:nil];
[(dumpFileHandle = [NSFileHandle fileHandleForWritingAtPath:dumpPath]) truncateFileAtOffset:0];
#endif
if (dumpFileHandle) [dumpFileHandle writeData:blah];
I want copy some files to Library or Document in the Product directory,
but when I add a build phase and select Products Directory in destination and input my subdir it's like Library/xxx/.
When I run the App in the simulator, I found nothing in the destination and if I set the destination to Resources, it will be there.
Can you please tell me why this is happening and how I can make it?
The documents directory should only be accessed at runtime. To my knowledge you cannot copy items there during building. What you should do is store those objects that you want in the Documents directory within your bundle and then copy them upon first launch.
Interesting question. I don't know if you can do this as part of the build phase.
To get something into the Documents directory, I have a method that runs at app startup like this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:#"filename.txt"];
BOOL success = [fileManager copyItemAtPath:defaultDBPath writablePath error:&error];
That's an incomplete fragment but you probably get the idea. The important thing is the NSDocumentDirectory constant.
Add a new build phase -> New Copy Files Build Phase in Target