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.
Related
Sorry I saw similar questions but they don't seem to have some full answers for me. And i try to put it in order so that people will not hate me or my poor english.
I am working with Xcode 4.2 with storyboard and ARC
I can read from my plist file. My task is simply to write back the updated value(s) to my plist file.
My plist is contain in "supporting files" sub folder of the main folder (where story-board is things goes). the file is call Global.plist and GlobalValue2 is a element of the file type string.
So the read file part looks like this
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:plistfile];
FirstValueTextBox.text = [[dict valueForKey:#"GlobalValue1"] stringValue];
learn it from some handy youtube video works just fine. updates the value to my text box.
The real problem comes in when I write back my plist file. When i try the following
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSMutableDictionary *dict = [NSMutableDictionary
dictionaryWithContentsOfFile:plistfile];
[dict setValue:#"ABC" forKey:#"GlobalValue2"];
SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];
[dict writeToFile:plistfile atomically:YES];
the result is I really saw a updated value pop up on the second text box, but the plist file remain unchanged.
The following are the break down of my questions and my guess for the problem
I try to use NSDictionary(not NSMutableDictionary) and call setValue (crash in runtime)
my guess: NSDictionary object itself is readonly so it crash me when i say add value. But why don't it error me when in coding time? if the object is readonly
I use NSMutableDictionary can call setValue. it doesn't crash me and when i call the updated value at "SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];" it really return me the updated value. but the content inside of the plist file is not changed. Which is the result I have right now.
my guess: after some search here and there I think "supporting files" is read only too. pure guess did see anyone directly talk about it.
I did try to move on a little more and some people talks about a "document folder" in Xcode that is a read and write place. I think people also talk about write a code to access that folder. Can someone show me the code here.
My last question, can I hook up my Xcode to that "document folder" or where can i see it(the real file folder structure is different from inside Xcode I think). So that i can see and edit my plist file for testing, and i can see the real result without using codes and stuff
I will be much appreciated if people can tell me my guess is right or wrong and the answer to my 3 and 4 question.
In order for your changes to be persisted in your plist, you would indeed need ot copy it from the resource bundle to the documents dirtectory on launch of the application, then use the plist in the documents to read and write.
Here is how you can copy the file:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:#"Global.plist"];
if ([fileManager fileExistsAtPath:plistPath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
[fileManager copyItemAtPath:resourcePath toPath:plistPath error:&error];
}
I'm doing:
NSString *current_path = [[NSBundle mainBundle] bundlePath];
NSString *string_path = [NSString stringWithFormat:
#"%#/filedstring", current_path];
my_string_ = [[NSKeyedUnarchiver unarchiveObjectWithFile:string_path] retain];
The archived string is the text from a UITextField which we unarchive here. I've tried with and without current_path.
This all works fine when running in simulator (class member NSString *my_string_ is not nil) but when run on my iPhone my_string_ is nil.
Why is that?
Thanks for the quick responses all.
Adding to Jason Coco's answer, archive to and unarchive here:
NSString *library_path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *username_path = [library_path
stringByAppendingPathComponent:#"Caches/filedstring"];
You can't write into the main bundle on the phone, it's not allowed. That's why you don't find your archive there later. The simulator, since it actually runs on Mac OS X doesn't work this way, so it will actually write the file.
If you need to write something, you have to use one of the writeable paths available to your application. For more information, see the iOS Application Programming Guide / The File System. If you're going to do iOS Application Development, you should definitely read and understand this entire document.
As #middaparka says, there is probably something wrong with that file. Here's how I would debug this problem:
First step,
[[NSFileManager defaultManager] fileExistsAtPath: string_path];
Second step,
NSError *err;
NSString *tmp = [NSString stringWithContentsOfFile:string_path encoding:NSUTF8StringEncoding error:&err];
NSLog(#"Contents of string %#",tmp);
Once you've done those, you should have a much clearer idea why your NSKeyedUnarchiver is failing.
Also, check out NSString's stringByAppendingPathComponent: method.
I'm having trouble figuring out why files in my iPhone app seem to persist, even when I've deleted them. Here's the code that's giving me trouble:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *folderPath = [bundlePath stringByAppendingPathComponent:#"filefolder"];
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:folderPath error:NULL];
This code is supposed to look at the folder "filefolder" and read its contents into fileNames. When I run this app the very first time, it will do this. But if I change the contents of filefolder (for instance if I add or delete files) and I build and run the app again, the array filenames will contain names of all the newly added files (good) but also contains names of all the files that were supposed to have been deleted (bad)!!
Can anyone help me understand why I'm seeing this behavior?
Did you do a "Clean" in XCode after adding/removing new items from the bundle? This usually solves the problem of "stale" resources.
I want to use pathForResource, but it doesn't look like it will create the path if one doesn't exist. Therefore I'm trying to create one manually by doing the following:
NSString *path = [NSString stringWithFormat:#"%#/%#.plist",[[NSBundle mainBundle] bundlePath],#"myFileName"];
I'm creating files dynamically, so I need to access them after I have Build and Run the application. But it puts the project in a unique id folder so the path comes out to something like:
/Users/RyanJM/Library/Application Support/iPhone Simulator/3.0/Applications/80986747-37FD-49F3-9BA8-41A42AF7A4CB/MyApp.app/myFileName.plist
But that unique id changes every time I do a build. What is the proper way to create a path that I can get to every time (even in the Simulator)?
Thanks.
Update: edited the question, hopefully to help anyone who comes across it in the future.
Update: IWasRobbed answered the proper way to get create a path URL. But the the best answer I've been able to find is from Brad Parks. Though, I do wish there was a cleaner way.
With the way you phrased your question, this is how you read a plist that has been included in the bundle before build:
NSString *propertyListPath = [[NSBundle mainBundle] pathForResource:SomeString ofType:#"plist"];
If you want to access the directories that each app has as a unique storage area for a file that you create AFTER build, you use this:
#define kFilename #”data.plist”
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
Then you can check for it and do some data handling here:
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// do something with data here
}
You would save yourself a lot of trouble if you bought/read Beginning iPhone 3 Development specifically chapter 11 where he goes over data persistence (which is where this example came from). It's a great book.
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.