Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

StevenHu

macrumors member
Original poster
Sep 3, 2009
80
0
Southern CA
The following code writes the textField text to a plist successfully, and restores it when the app is turned off then on.

But when I uncomment the following two lines to add it to the persistence code ...

Code:
//	[array addObject:labelShockMountingTower.text];

//	labelShockMountingTower.text = [array objectAtIndex:2];

... then I get the following error:

Code:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (2) beyond bounds (2)'


I don't know what to change to get rid of this error. The text is word-for-word out of the book, Beginning iPhone Development by Dave Mark and Jeff LaMarche from Apress, except I am using my own field names.

Thanks,
Steve

Code:
// Following is from chapter 11 of Beginning iPhone Development, for data persistence
-(NSString *)dataFilePath
{
	NSArray *paths = NSSearchPathForDirectoriesInDomains(
														 NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectory = [paths objectAtIndex:0];
	return [documentsDirectory stringByAppendingPathComponent:kFilename];
}

-(void)applicationWillTerminate:(NSNotification *)notification
{
	NSMutableArray *array = [[NSMutableArray alloc] init];
	[array addObject:textFieldRounded.text];
	[array addObject:textFieldNotes2.text];
//	[array addObject:labelShockMountingTower.text];
	[array writeToFile:[self dataFilePath] atomically:YES];
	[array release];
	
}




	- (IBAction)segmentActionShockMountingTower:(id)sender
	{
		
		switch ([((UISegmentedControl *)sender) selectedSegmentIndex]) 
		{
			case 0:
				labelShockMountingTower.text = NSLocalizedString(@"1", @"ButtonOption");
				break;
			case 1:
				labelShockMountingTower.text = NSLocalizedString(@"2", @"ButtonOption");
				break;
			case 2:
				labelShockMountingTower.text = NSLocalizedString(@"3", @"ButtonOption");
				break;
			default:
				labelShockMountingTower.text = NSLocalizedString(@"2", @"ButtonOption");
				break;
		}
		return;
	}	







- (void)viewDidLoad {

// ... misc. app code is here...


	NSString *filePath = [self dataFilePath];
	if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
	{
		NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
		textFieldRounded.text = [array objectAtIndex:0];
		textFieldNotes2.text = [array objectAtIndex:1];
//		labelShockMountingTower.text = [array objectAtIndex:2];
		[array release];
	}
	
	UIApplication *app = [UIApplication sharedApplication];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(applicationWillTerminate:)
												 name:UIApplicationWillTerminateNotification object:app];
	
	
	[super viewDidLoad];
	
}
 
Did you remember to connect labelShockMountingTower to the UITextField in IB?

If so, instead, maybe trying logging what [array count] is before writing it to the file.
 
Did you remember to connect labelShockMountingTower to the UITextField in IB?

If so, instead, maybe trying logging what [array count] is before writing it to the file.

No, the text field and control label are not related to each other. The data the control produces goes into the control's label, not the text field. Sorry that wasn't clear before.

Thanks,
Steve
 
No, the text field and control label are not related to each other. The data the control produces goes into the control's label, not the text field. Sorry that wasn't clear before.
So, labelShockMountingTower is a UILabel? Still, is it connected to the correct property in IB?
 
Everything is connected in IB properly. It all works fine. I am adding the persistence code to a working project. Then I get errors with the UILabel.

Thanks,
Steve
 
I did this:

NSLog(@"array is %@", array);

and got the correct string contents of the array's two lines.
Shouldn't array contain three items at that point?

EDIT: I wouldn't be surprised if you ran the code with it writing two items and since the file now exists, it is trying to read three items. If so, start with a fresh file (i.e. the one at kFilename).
 
Shouldn't array contain three items at that point?

It will contain two objects if the third object was commented out.
I uncommented the third line and still got just the first two array objects.

I wonder why the third object is not making it in?

Thanks,
Steve
 
Well, I swapped the second and third objects. The array was still the first two objects and not the third one (textfield).

Steve
 
Well, I swapped the second and third objects.
What do you mean you swapped them? Does that mean the code is now:
Code:
	[array addObject:textFieldRounded.text];
	[array addObject:labelShockMountingTower.text];
	[array addObject:textFieldNotes2.text];
?

Please try and be as specific as possible in what you are doing / attempting. That helps us with your troubleshooting.
 
What do you mean you swapped them? Does that mean the code is now:
Code:
	[array addObject:textFieldRounded.text];
	[array addObject:labelShockMountingTower.text];
	[array addObject:textFieldNotes2.text];
?

Please try and be as specific as possible in what you are doing / attempting. That helps us with your troubleshooting.

Yes, that order is correct. I did that to test whether the UILabel could be a problem. It isn't.

I notice that with the third line commented out, the log displays in the debugger (gdb). But when the third line is uncommented, the log array does not appear in the debugger. I get the error msg instead (which appears when I click on the table cell that will introduce the view this code is on).

Thanks,
Steve
 
I notice that with the third line commented out, the log displays in the debugger (gdb). But when the third line is uncommented, the log array does not appear in the debugger. I get the error msg instead (which appears when I click on the table cell that will introduce the view this code is on).
Then please provide the code for the entire method that is doing the writeToFile: as well as the code for your didSelectRowAtIndexPath:
 
Then please provide the code for the entire method that is doing the writeToFile: as well as the code for your didSelectRowAtIndexPath:

The entire writeToFile code for the .m file is in the first post.

I'll get you the other code after 1pm. I'm going out.

Thanks,
Steve
 
You sure that's your code in post #1? I can't help but notice a syntax issue at

Code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(

Also, if any of those objects are nil, they won't be added to the array that is eventually persisted. So I share the same suspicions as dejo and think its your bindings.

I'd also suggest more validation checks (nil checking, boundaries, etc) before saving/fetching.
 
You sure that's your code in post #1? I can't help but notice a syntax issue at

Code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(

Also, if any of those objects are nil, they won't be added to the array that is eventually persisted. So I share the same suspicions as dejo and think its your bindings.

I'd also suggest more validation checks (nil checking, boundaries, etc) before saving/fetching.

The code in the first post was copied out of Xcode, into TextEdit, and into the forum as is. I double-checked with the book and found it letter-perfect.

Thanks!
Steve
 
Then please provide the code for the entire method that is doing the writeToFile: as well as the code for your didSelectRowAtIndexPath:


Code:
-(NSString *)dataFilePath
{
	NSArray *paths = NSSearchPathForDirectoriesInDomains(
														 NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectory = [paths objectAtIndex:0];
	return [documentsDirectory stringByAppendingPathComponent:kFilename];
}

-(void)applicationWillTerminate:(NSNotification *)notification
{
	NSMutableArray *array = [[NSMutableArray alloc] init];
	[array addObject:textFieldRounded.text];
	[array addObject:labelShockTowerMaterial.text];
//	[array addObject:textFieldNotes2.text];
	[array writeToFile:[self dataFilePath] atomically:YES];
	
	NSLog(@"array is %@", array);
	[array release];

}

Here's the code in the view controller that will open the view with the above code.

Code:
...

	FrontSuspension1 *frontSuspension1 = [[FrontSuspension1 alloc]
										  initWithNibName:@"FrontSuspension1" bundle:nil];
	[self.menuList2 addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
							   @"Front End", kTitleKey,
							   frontSuspension1, kViewControllerKey,
							   nil]];
	[frontSuspension1 release];
...



- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
	UIViewController *targetViewController = [[self.menuList2 objectAtIndex: indexPath.row] objectForKey:kViewControllerKey];
	[[self navigationController] pushViewController:targetViewController animated:YES]; /*YES means the row will slide to left when tapped. NO means the next view will appear instantly. */
}
...

Many thanks for your attention!
Steve
 
I changed the kFilename from data.plist to data2.plist. Now there are no errors! I am going to add more of the controls to the array and see what happens.

Steve
 
I added all the controls' labels to the array, changed the kFilename to data3.plist, and it worked without errors. It saved all the user-selected data when it powered off, and showed all the data in the labels when the page was accessed. This worked in the Simulator and device.

Changing kFilename did the trick, though I don't know why.

Thanks,
Steve
 
Changing kFilename did the trick, though I don't know why.
Something related to this, I would imagine:
EDIT: I wouldn't be surprised if you ran the code with it writing two items and since the file now exists, it is trying to read three items. If so, start with a fresh file (i.e. the one at kFilename).
You might want to consider checking the return value of writeToFile:atomically: and doing some error-handling if it returns NO. Also, when processing array, add some error-handling for when things are not as expected, such as too-few items. I might even process array in a for-loop to make sure it is only handling the items that are actually there.
 
Those are good ideas!
Yeah, usually such error-handling is left out of the intro books because they tend to get in the way of whatever subject it is they are teaching. But when it comes to your own code, it's always a good idea to be as robust as you can. When you can, check return values and if you are passing in NSErrors, check them on the way out.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.