I want to write a file in the Documents folder of my iPhone app.
But when I try to open the path I get the
Cocoa error 257 - Permission Denied.
Here's a little code snippet:
// create path to file
NSURL *path = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
NSLog(#"%#", [path description]);
NSError *error = nil;
NSString *filename = [NSString stringWithContentsOfURL:path encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", error);
NSLog(#"%#", [filename description]);
Can you tell me please, what's wrong with that code?
Thanks!
It looks as though you're trying to open the documents directory rather than a file in that directory. stringWithContentsOfURL loads the contents of the file into the NSString, so it's not a filename.
If filename should be a path rather than the contents of a file, use stringWithContentsOfURL: instead.
Related
I'm writing an application for the Apple watch. I'm using the following method (from this SE answer) to write to a log file:
- (void) writeLogWith: (NSString *) content {
//Get the file path
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"whathappened.md"];
//create file if it doesn't exist
if(![[NSFileManager defaultManager] fileExistsAtPath:fileName])
[[NSFileManager defaultManager] createFileAtPath:fileName contents:nil attributes:nil];
//append text to file (you'll probably want to add a newline every write)
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:fileName];
[file seekToEndOfFile];
[file writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
[file closeFile];
return;
}
I'm running it by plugging in my phone and running it directly on my watch. The function is definitely executing (I've stepped thought with the debugger) and it also knows that the file exists and doesn't repeatedly try and create it.
Xcode tells me that the file information is:
Printing description of documentsDirectory:
/var/mobile/Containers/Data/PluginKitPlugin/8503AD6C-6EC9-4522-A867-27109B01B615/Documents
Printing description of documentsDirectory:
(NSString *) documentsDirectory = 0x16d66890
Printing description of fileName:
(NSString *) fileName = 0x16d66950
Printing description of fileName:
/var/mobile/Containers/Data/PluginKitPlugin/8503AD6C-6EC9-4522-A867-27109B01B615/Documents/whathappened.md
I'd like to know if it's writing things correctly, but when I look at the container (following these SE answers), the documents directory is empty.
My question is: where did my file go? And how can I find it?
I think what might be giving you problems is the NSFileHandle object. I have written thousands upon thousands of files to the documents folder and I have never used NSFileHandle to do this. Simply use the built in method on NSString to write your string to the file path.
Try this:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [[documentsDirectory stringByAppendingPathComponent:#"whathappened"] stringByAppendingPathExtension:#"md"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
}
NSString *string = #"The String You Want To Write.";
NSError *error;
[string writeToFile:filePath atomically:false encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(#"There was an error writing file\n%#", error.localizedDescription);
}
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSLog(#"File exists :)");
}
else {
NSLog(#"File does not exist :(");
}
From other research, the answer appears to be:
If you are storing a file on the Apple Watch, it is stored in it's own container, which isn't visible via xcode.
It indeed, appears to be related to this bug report: https://openradar.appspot.com/radar?id=5021353337946112, found via this SE: How to export shared container of an iOS App with Xcode6.2?
I am writing a registration app that is supposed to save a CSV file in the documents directory folder. I would like to look at the output and see what happens when I open the CSV file in excel. I navigated to the documents directory folder by finding out where it should be saved using this code snippet:
NSLog(#"Info Saved");
NSLog(#"Documents Directory: %#", [[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject]);
Here is my code for saving the information put into the 11 text fields in the registration form:
- (IBAction)saveFormButton:(id)sender {
// saves text field data in comma separated CSV file format
NSString *formData = [NSString stringWithFormat:#"%#,%#,%#,%#,%#,%#,%#,%#,%#,%#,%#\n",
self.nameTextfield.text, self.emailTextfield.text,
self.phoneTextfield.text, self.termTextfield.text,
self.schoolTextfield.text, self.graduationTextfield.text,
self.gpaTextfield.text, self.degreeTextfield.text,
self.interestTextfield.text, self.groupTextfield.text,
self.appliedTextfield.text];
// get document directory path
NSString *documentDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES)objectAtIndex:0];
// append results.csv onto doc path
NSString *event = [documentDirectoryPath stringByAppendingString:#"results.csv"];
// creates folder if it does not exist
if (![[NSFileManager defaultManager] fileExistsAtPath:documentDirectoryPath]) {
[[NSFileManager defaultManager] createFileAtPath:event contents:nil attributes:nil];
}
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:event];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[formData dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
Should I be seeing a file in that specific folder I have navigated to?
Thank you for your help,
Change this line:
NSString *event = [documentDirectoryPath stringByAppendingString:#"results.csv"];
to:
NSString *event = [documentDirectoryPath stringByAppendingPathComponent:#"results.csv"];
This makes sure that the path is correctly formatted. Also, you seem to be checking to see if "documentDirectoryPath" exists before creating the file rather than the filename itself. Change:
if (![[NSFileManager defaultManager] fileExistsAtPath:documentDirectoryPath]) {
[[NSFileManager defaultManager] createFileAtPath:event contents:nil attributes:nil];
}
to:
if (![[NSFileManager defaultManager] fileExistsAtPath:event]) {
[[NSFileManager defaultManager] createFileAtPath:event contents:nil attributes:nil];
}
Here is a more elegant way with less code
// Content of file
NSString* str= #"str,hey,so,good";
// Writing
NSString *root = [NSHomeDirectory() stringByAppendingPathComponent:#"file.csv"];
[str writeToFile:root atomically:YES encoding:NSUTF8StringEncoding error:NULL];
// Reading
NSString *string = [[NSString alloc] initWithContentsOfFile:root encoding:NSUTF8StringEncoding error:nil];
NSLog(#"%#",string);
The result:
2015-07-15 15:52:56.267 ObjC[2927:15828] str,hey,so,good
I am creating an iPhone app that writes to a CSV file. It seems the most simple method of this would be to add to an NSMutableString from an array. The code I have should be working expect I keep getting Cocoa error 513
The code is:
NSArray *firstArray=[NSArray arrayWithObjects:#"A",#"b",nil];
NSMutableString *csv = [NSMutableString stringWithString:#"Strings"];
NSUInteger count = [firstArray count];
for (NSUInteger i=0; i<count; i++ ) {
[csv appendFormat:#"\n %#",
[firstArray objectAtIndex:i]
];
}
NSString *yourFileName = #"leads.csv";
NSError *error;
BOOL res = [csv writeToFile:yourFileName atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!res) {
NSLog(#"Error %# while writing to file %#", [error localizedDescription], yourFileName );
}
Thank you so much!
It is most likely because you are writing to a file location that you do not have permission to write.
Cocoa error 513 translates to the error NSFileWriteNoPermissionError.
Typically, this occurs when someone tries to write to a file within the applications bundle. You cannot modify the contents of a compiled app's bundle folder. This is because the bundle is a signed, compiled application.
When you eventually distribute the app through the iTunes App Store, the application has a digital signature that validates the contents of the app. This signature is generated at compile time and once signed, Apple does not want anyone tampering with the contents.
Make sure you are writing to an appropriate location, like Documents, Temp and Cache using something like the following:
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"subFolder"];
NSString *filePath = [dataPath stringByAppendingPathComponent:#"fileName.csv"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
{
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
}
BOOL res = [csv writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!res) {
NSLog(#"Error %# while writing to file %#", [error localizedDescription], yourFileName );
}
These folders are only accessible to your app. No other app can access the contents of these folders. (Likewise, your app cannot access another app's folders.)
I'm trying to copy a json file from the application bundle to the Document directory, but surprisingly i'm not able to do that. Here's the code:
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [array lastObject];
NSString *path = [docDir stringByAppendingPathExtension:#"themes.json"];
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
{
NSString *themesPath = [[NSBundle mainBundle] pathForResource:#"themes" ofType:#"json"];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:themesPath toPath:path error:&error];
if (error)
NSLog(#"Error %#", error);
}
It produces the following error:
Error Domain=NSCocoaErrorDomain Code=513 "The operation couldn’t be completed. (Cocoa error 513.)" UserInfo=0x194a0e90 {NSSourceFilePathErrorKey=/var/mobile/Applications/ACED3EF9-B0D8-49E8-91DE-37128357E509/Frinder.app/themes.json, NSUserStringVariant=(
Copy
), NSFilePath=/var/mobile/Applications/ACED3EF9-B0D8-49E8-91DE-37128357E509/Frinder.app/themes.json, NSDestinationFilePath=/var/mobile/Applications/ACED3EF9-B0D8-49E8-91DE-37128357E509/Documents.themes.json, NSUnderlyingError=0x194a0400 "The operation couldn’t be completed. Operation not permitted"}
After a search i've found this question and tried to modify my code as follows:
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
{
NSString *themesPath = [[NSBundle mainBundle] pathForResource:#"themes" ofType:#"json"];
NSData *data = [NSData dataWithContentsOfFile:themesPath];
BOOL success = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
if (!success)
NSLog(#"Fail");
}
but it doesn't work either. success variable is NO. The last thing i tried was:
[data writeToFile:path atomically:YES];
but still in vain. it returns NO.
I need to note that the issue arises on a device only. On a simulator it works all right.
Can anybody give me a clue?
The first example is correct on one thing:
[docDir stringByAppendingPathExtension:#"themes.json"];
should be:
[docDir stringByAppendingPathComponent:#"themes.json"];
This becomes clear we you read the error message, you see that it tries to write the file to /var/mobile/Applications/ACED3EF9-B0D8-49E8-91DE-37128357E509/Documents.themes.json. Notice that there a . where there should be a /.
The stringByAppendingPathExtension: is used for adding a extension to a file, jpg, txt, html,.....
stringByAppendingPathComponent will add a file to a path, by adding the correct directory separator, you case /.
You second example will definitely fail since the app bundle is readonly.
I'm trying to use the method seen in the solution here to delete all the files in the iPhone documents directory for the application I'm writing. I've made some minor changes to the code in the solution in order to pass in the string location of the documents directory. My version of the code is the following:
NSString *directory = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] absoluteString];
NSLog(#"%#", directory);
NSError *error = nil;
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directory error:&error];
if (error == nil) {
for (NSString *path in directoryContents) {
NSString *fullPath = [directory stringByAppendingPathComponent:path];
BOOL removeSuccess = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
if (!removeSuccess) {
// Error handling
}
}
} else {
// Error handling
NSLog(#"%#", error);
}
When I attempt to run this however, the setting of directoryContents fails due to what's being passed being interpreted as a non-existent directory. Specifically, the two NSLog() statements I have put in the code returns the following:
2013-04-22 11:48:22.628 iphone-ipcamera[389:907] file://localhost/var/mobile/Applications/AB039CDA-412B-435A-90C2-8FBAADFE6B1E/Documents/
2013-04-22 11:48:22.650 iphone-ipcamera[389:907] Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0x1d5232c0 {NSUnderlyingError=0x1d54c420 "The operation couldn’t be completed. No such file or directory", NSFilePath=file://localhost/var/mobile/Applications/AB039CDA-412B-435A-90C2-8FBAADFE6B1E/Documents/, NSUserStringVariant=(
Folder
)}
As far as I can see the path being printed to the NSLog looks correct so I'm not sure what I'm doing wrong. Can anyone point out to me where my mistake is? Thank you much!
Your code to get the value of directory isn't quite right. You want:
NSURL *directoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSString *directory = [directoryURL path];
Calling absoluteString on NSURL gives you a file URL. You don't want a file URL, you want the file URL converted to a file path. This is what the path method does.
Another way is:
NSString *directory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];