Crash Writing NSUserDefaults from applicationDidEnterBackground

I've been seeing a lot of crash reports on iOS 15 for code that has been in place a long time. The crash report indicates this is happening during the call to saveAppData while trying to persist the state of the users tab order. There is a More Controller in the mix but this was working for literally years.

I've been staring at this for days and can't find anything obvious. I am wondering if something changed with iOS 15 which is when this issue started . This is the relevant code:

-(void)saveAppData
{
	// Save Current Tab Order to Users Prefs
	NSMutableArray *savedOrder = [NSMutableArray arrayWithCapacity:TAB_BAR_NUMBER_OF_ITEMS];
	NSArray *tabOrderToSave = _tabBarController.viewControllers;
	
	for(UIViewController *aViewController in tabOrderToSave) {
		[savedOrder addObject:@(aViewController.tabBarItem.tag)];
	}
	[userSettings setObject:savedOrder forKey:SavedTabOrderKey];
   
  // Save the TAG of selected TabBarItem
  [userSettings setObject:@(_tabBarController.tabBar.selectedItem.tag) forKey:SavedTabLastViewedKey];
	[userSettings synchronize];
}

-(void)applicationDidEnterBackground:(UIApplication *)application
{
     NSLog(@"**** Entering Background Saving Data");
     [self saveAppData];
     [self.coreDataUtil saveMOC];
}

and here is the crash report. Any assistance on observations are most welcome. Thanks.

Answered by cliffr in 737151022

The root cause of this was that a ViewController that was dismissed by the time the application went into the background was still registered as an Observer of NSUserDefaults and when the app saved state that class was called and -> CRASH!

Where is the crash log ?

You haven't mentioned where in the method it's crashing, but you should do some debugging with breakpoints and inspecting the variables, or some simple stuff with NSLog.

Maybe the view controllers have already disappeared so you can't access them.

Maybe _tabBarController has gone?

Maybe userSettings has gone and you're trying to save into it?

Maybe the savedOrder mutable array has the wrong size and you're trying to add an extra item? (I'd just init it and add objects to it rather than create it with a given size, and expect there to be a set number of items.)

Also, this:

    NSArray *tabOrderToSave = _tabBarController.viewControllers;
	
    for(UIViewController *aViewController in tabOrderToSave) {

could be:

    for(UIViewController *aViewController in _tabBarController.viewControllers) {

I spent quite a while logging items, but here again, I have never actually seen this crash in Xcode or on any device or it would have been easier to find. I had a theory that perhaps the more controller or one of the tabs on it was not nilled out but could never prove it. The thing that is puzzling is that the documentation for applicationDidEnterBackground states that that is the place to do quick things like save state. And that is all this method is doing, it is saving the order of the users tabs and the last one selected so that it can remember it for next time.

The symbolicated crash log indicates it crashes on the second to last line of saveAppData, the last call to setObject before the synchronize.

Anyway darkPaw, thanks for your ideas. These are all good suggestions and they definitely got me thinking in probably the right direction. I don't know yet if they are the "RIGHT" answer but they are good suggestions.

But!! I did find this in the documentation for the tabBarController:

In iOS 6 and later, if you assign a value to this view controller’s restorationIdentifier property, it preserves a reference to the view controller in the selected tab. At restore time, it uses the reference to select the tab with the same view controller.

My code is older than iOS 6, to give you an idea how long that has been there :)- LOL and I missed the bit about state preservation. Probably I should just let it do it for me and not have my own code for that.

Full log now attached.

Accepted Answer

The root cause of this was that a ViewController that was dismissed by the time the application went into the background was still registered as an Observer of NSUserDefaults and when the app saved state that class was called and -> CRASH!

Crash Writing NSUserDefaults from applicationDidEnterBackground
 
 
Q