Persistence with plist stops working with addition of another object

Discussion in 'iOS Programming' started by StevenHu, Mar 23, 2010.

  1. StevenHu macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #1
    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];
    	
    }
    
     
  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    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.
     
  3. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #3
    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
     
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    So, labelShockMountingTower is a UILabel? Still, is it connected to the correct property in IB?
     
  5. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #5
    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
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    Did you try this yet?
     
  7. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #7
    How do I do that?

    I did this:

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

    and got the correct string contents of the array's two lines.

    Thanks,
    Steve
     
  8. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #8
    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).
     
  9. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #9
    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
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    This means that labelShockMountingTower.text == nil, at that point.
     
  11. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #11
    Well, I swapped the second and third objects. The array was still the first two objects and not the third one (textfield).

    Steve
     
  12. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #12
    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.
     
  13. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #13
    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
     
  14. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #14
    Then please provide the code for the entire method that is doing the writeToFile: as well as the code for your didSelectRowAtIndexPath:
     
  15. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #15
    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
     
  16. jeremy.king macrumors 603

    jeremy.king

    Joined:
    Jul 23, 2002
    Location:
    Fuquay Varina, NC
    #16
    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.
     
  17. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #17
    But that doesn't contain the NSLog(), which you claim is being skipped when the third line is uncommented. So, we need to see the latest code.
     
  18. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #18
    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
     
  19. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #19

    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
     
  20. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #20
    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
     
  21. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
    #21
    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
     
  22. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #22
    Something related to this, I would imagine:
    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.
     
  23. StevenHu thread starter macrumors member

    Joined:
    Sep 3, 2009
    Location:
    Southern CA
  24. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #24
    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.
     

Share This Page