NSString WriteToFile not stay permanent? - cocoa-touch

I am trying to save text stored in an NSString variable in a text file that is stored with the main bundle of my project.
So far I have had no success and tried a lot of different methods.
Why doesn't this stay permanent?
NSString *pathToFile = [[NSString alloc]init];
pathToFile = [[NSBundle mainBundle] pathForResource:#"ListOfSavedImages" ofType:#"txt"];
NSLog(#"%#",pathToFile);
NSString *stringToWriteToFile = [[NSString alloc]init];
stringToWriteToFile=#"Adam";
NSLog(#"%#",stringToWriteToFile);
[stringToWriteToFile writeToFile:pathToFile atomically:YES encoding:NSUTF8StringEncoding error:NULL];
NSLog(#"called!");
NSString *contentsOfFile1 = [NSString stringWithContentsOfFile:pathToFile encoding:NSUTF8StringEncoding error:NULL];
NSLog(#"%#",contentsOfFile1);
The actual file doesn't change although the NSLog at the end of this code segment outputs "Adam" but I am also nslogging the contents of the file when the view loads and it always reverts back to the original text(it never actually changes). What am I doing wrong?
I am using Xcode 4.3, ARC, and storyboards.

As you are instantiating your variables locally, they will leak away when you hit the end of the block }.
Try using IVars declared as properties of the particular view controller, synthesized in the .m file.
Look at the C139p at Stanford Course on ITunes, preferably the earlier series given before ARC as this fully explains the concept of data persistence.

Related

Why my program can run in Xcode, but cannot running as a separate app?

My program loads some data from a file and then draws them.
The file-reading part is like this:
- (void)load_file
{
NSFileHandle *inFile = [NSFileHandle fileHandleForReadingAtPath:#"map_data"];
NSData *myData=[inFile readDataToEndOfFile];
NSString *myText=[[NSString alloc]initWithData:myData encoding:NSASCIIStringEncoding];
NSArray *values = [myText componentsSeparatedByString:#"\n"];
for (NSString *string in values) {
NSArray *lines=[string componentsSeparatedByString:#" "];
if ([lines count] != 2) break;
NSPoint point= NSMakePoint([lines[0] floatValue], [lines[1] floatValue]);
[points addObject:[NSValue valueWithPoint:point]];
}
[self setNeedsDisplay:YES];
}
When debugging, I put the data file in the directory of [NSBundle mainBundle], and the program works fine.
However, when I use achieve to take the app out, it never runs. I put the data file in the same path with the app, but it seems fail to load it.
Update
I tried to use c++, but still fails.
- (void)load_file
{
ifstream inf("map_data");
double x, y;
while (inf >> x >> y) [points addObject:[NSValue valueWithPoint:NSMakePoint(x, y)]];
inf.close();
}
I tried to change the build scheme to release and run, which is fine. But whenever I go directly into the finder of app and double click it, it does not work and seems nothing is loaded.
add the file to the project as a Resource (this will cause it to be copied into the app wrapper in the right spot)
use `[[NSBundle mainBundle] pathForResource:#"map_data" ofType:nil];
That should give you the path to the file. The file should not be manually copied, it should not be next to the app wrapper, nor should you [conjecture] ever try changing or replacing the file once it is in your app wrapper.
The reason why it seems to work sometimes is mere coincidence. You are passing a partial path to NSFileHandle and it happens that the current working directory of your app sometimes points to the right spot such that the data file is available.
I'm not sure how relative paths are handled by NSFileHandle, but usually you set up paths using the NSBundle class.
NSString *path = [[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"ext"];
You can also simply initialize an NSString from the contents of a file, you don't need to first read it into an NSData using NSFileHandle.
NSString *text = [[NSString alloc] initWithContentsOfFile:path
encoding:NSASCIIStringEncoding error:nil];
(Use the error parameter, if you want proper error handling)

Reading strings from txt files into NSArrays in iOS

After much reading it seems that, really, the only way to read a number of lines from a text file into an NSArray is with this:
NSString *myfilePath = [[NSBundle mainBundle] pathForResource:#"poem" ofType:#"txt"];
NSString *linesFromFile = [[NSString alloc] initWithContentsOfFile:myfilePath encoding:NSUTF8StringEncoding error:nil];
myArrayOfLines = [NSArray alloc];
myArrayOfLines = [linesFromFile componentsSeparatedByString:#"\n"];
NSArrays have a method for initWithContentsOfFile but I have not seen any examples of how to use this. I have read some posts that state that the file must be a plist and not a generic txt file.
Is this really the case? Is there a way to read lines (terminated with \n) directly into an NSArray?
You have it right, except the line myArrayOfLines = [NSArray alloc]; which is useless.
Don't bother with plist if you already have a good txt file.
But for curiosity, here is a link which explains how it works with plist files : link
Also, if you don't use ARC, you'll have some leaks, but that's another question, and we don't have the whole code, so I might be wrong.

Reused code to read from file causes EXC_BAD_ACCESS only in second class

I use this code in two different files to fill the categories array from a string of text from a text file, in which entries are separated by double pipes.
In the first file, my appViewController class, everything is fine. In the second, popoverViewController, the program bombs with EXC_BAD_ACCESS on the arrayWithArray: line. Declarations for categories, tempArray, diskfile, and textFromFile are the same in both interface files.
NSLog tracers and breakpoints confirmed values of variables are the same down to that last fatal line. The popover contains a picker, so picker delegate and datasource protocols are in place. That's the only difference. Can anyone explain what might be going on?
categories=[[NSMutableArray alloc] init];
tempArray = [[NSMutableArray alloc] init] ;
NSMutableString *textFromFile=[[NSString alloc] init];
NSString *filePath = [[NSBundle mainBundle] pathForResource: #"Categories" ofType: #"txt"];
if (filePath) {
textFromFile = [NSString stringWithContentsOfFile:filePath];
categories=[NSMutableArray arrayWithArray:[textFromFile componentsSeparatedByString: #"||"]];
}
Set NSZombieEnabled, MallocStackLogging, and guard malloc in the debugger. Then, when your App crashes, type this in the gdb console:
(gdb) info malloc-history 0x543216
Replace 0x543216 with the address of the object that caused the crash, and you will get a much more useful stack trace and it should help you pinpoint the exact line in your code that is causing the problem.
See this article for more detailed instructions.

iOS/iPhone SDK: initWithCoder and encodeWithCoder not being called

I'm trying to save an NSMutableArray called queueArray so it can be loaded again after the app has been quit. I used a few tutorials to get me going and this is the code I have come up with. The problem seems to be that "initWithCoder" and "encodeWithCoder" are not being called, shown by no NSLog calls and no stopping at breakpoints. I have added the NSCoding protocol to the .h file and I know that queueArray is not nil and it contains MPMediaItems. Here is some of the code I use to try to save and load the array:
-(IBAction)saveQueuePressed {
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [rootPath stringByAppendingPathComponent:#"queueArray.archive"];
//should cause encodeWithCoder to be called
[NSKeyedArchiver archiveRootObject:queueArray toFile:filePath];
}
-(IBAction)loadQueuePressed {
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [rootPath stringByAppendingPathComponent:#"queueArray.archive"];
//should cause initWithCoder to be called
queueArray = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
}
-(void)encodeWithCoder:(NSCoder *)coder {
NSLog(#"encodeWithCoder");
[coder encodeObject:queueArray forKey:#"savedQueueArray"];
}
-(id)initWithCoder:(NSCoder *)decoder {
NSLog(#"initWithCoder");
queueArray = [decoder decodeObjectForKey:#"savedQueueArray"];
return self;
}
Any help will be greatly appreciated!
The encodeWithCoder: and initWithCoder methods are called when you archive/unarchive an object that responds to them. From what I understand, you have those methods in your class, but the object you are actually archiving (queueArray) is not an instance of that class, it's an NSMutableArray.
If you do want to save your entire object, you can change your saveQueue method to this
-(IBAction)saveQueuePressed {
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [rootPath stringByAppendingPathComponent:#"queueArray.archive"];
// saving the array shouldn't, but saving the self object
//should cause encodeWithCoder to be called:
[NSKeyedArchiver archiveRootObject:self toFile:filePath];
}
But if you just want to save the array, I guess you can just use saveQueuePressed and loadQueuePressed, I don't think you need the encode/init WithCoder: methods
Update:
Maybe your path is not right.
Try
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [[rootPath stringByAppendingPathComponent:#"queueArray.archive"] stringByExpandingTildeInPath]];
Filipe is right! Your comment said you still didn't use his method.
I had this issue too. Switching from the dictionary's atomic write method to the keyedArchiver fixed it, luckily I only had to change one thing, the line that said writeToFile: is now the archive function.
Now my program's working. For some reason, even when responding to NSCoding, the custom object is not being encoded and breaks my dictionary. Is this a bug with iOS? I've read a fair number of Apple Manuals, but I've also seen a fair number of typos and missing info (For example, try MKMapRect functions without the videos to explain them), or Core Animations referencing the Run Loop before you learn threading, I could go on, half finished sentences in Quartz... so yeah, I've read the manuals and this perplexes me, we have to get a more open iOS SDK at some point, hopefully

Converting File Path From NSString To NSURL

I'm working through Cocoa smoothly, but this problem seems so basic it cancels out all the cool stuff I learned. :/
I have a generated file path, and it needs to be in NSURL format. From research, this is the code I wrote:
NSLog(#"Old path = %#", pathToFile);
NSURL *xmlURL = [[[NSURL alloc] init] fileURLWithPath:pathToFile];
NSLog(#"New path = %#", [xmlURL absoluteString]);
And the output:
2010-01-27 15:39:22.105 MusicLibraryStats[28574:a0f] Old path = file://localhost/Users/[username]/Music/iTunes/iTunes%20Music%20Library.xml
2010-01-27 15:39:22.105 MusicLibraryStats[28574:a0f] New path = (null)
First off, the alloc-init shouldn't even be necessary; other people seem to get away with it. In this case, if I don't alloc-init, I get an 'unrecognized selector' error on that line. Of course, now I'm just getting plain old (null).
Where did I goof?
Thanks!
The [[NSURL alloc] init] is not just unnecessary, it's invalid. fileURLWithPath: is a class method, which means you can only call it on the class object (that is, NSURL itself). It does not produce a compile error because -(NSURL *)init returns an object of type id, and does not result in a runtime error because -(NSURL *)init actually returns nil, and messages sent to nil will just cascade another nil as their return value.
This code should work:
NSString* pathToFile = #"/this/is/a/path";
NSURL* url = [NSURL fileURLWithPath:pathToFile];
I found your problem.
-[NSOpenPanel URLs] returns an array of NSURL objects, which you treat as NSString objects. That's not right. You should use the following:
NSURL* url = [[oPanel URLs] objectAtIndex:0];
The debugger could've show you that if you looked at the pathToFile variable. Make sure to check it next time. :) Hovering a variable with your mouse should get you its type.
However, remember that there are situations where you will legitimately encounter another type than the one you expected. For instance, the private NSPathStore2 class is part of the NSString cluster, and you can do everything NSString supports on NSPathStore2 objects. (If this happens and you're not too sure, check the documentation to see if the type you expect is a cluster type. That's how they're called in the documentation.)