@jamie_sq or anyone have any thoughts regarding this issue related to NSFetch and sectionHeaders?
The code in question has worked for years but started failing on iOS 16.
After seeing this post and realizing it had something to do with section ordering and seeing that it seems to only happen on devices using foreign languages we looked more carefully at our code and realized that the nameFirstLetter method of our NSManagedObject which is the ONLY Entity in our CoreData database and which returns the string used for sectionNameKeyPath was not properly handling Unicode characters.
nameFirstLetter method simply grabs the first character of NAME field on the NSManagedObject. Name is an actual stored field and this method takes the first character and returns it. We improved this to use rangeOfComposedCharacterSequenceAtIndex. That works ok until we tried to do [str localizedUppercaseString] on it. And NOW we see exactly the crash our customers are seeing.
-(NSString *)nameFirstLetter
{
if(self.name != nil) {
return [self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]];
/* The above works with the issue that sections show up for lower case letters and then again for uppercase.
As soon as we do the following the NSFetchResultController CRASHES with the _computeSectionInfo error */
return [[self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]] localizedUppercaseString];
}
else
return @"?";
}
We don't understand why this should be a problem given that the NSFetchResultController is initialized to do localizedCaseInsensitiveCompare? This is how we set up the FetchControllers sorts.
-(void)initializeFetchedResultsController
{
// Already inititialized
if(_fetchedResultsController != nil) return;
// Create and configure a fetch request for Charts
NSManagedObjectContext *moc = ((iPhemerisAppDelegate *)[[UIApplication sharedApplication] delegate]).coreDataUtil.pc.viewContext;
if(!moc) {
NSLog(@"*** SavedChartView MOC NOT READY");
return;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Charts" inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *chartTypeDescriptor = [[NSSortDescriptor alloc] initWithKey:@"chartType" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, chartTypeDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_sectionNameKeyPath = @"nameFirstLetter";
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:_sectionNameKeyPath cacheName:nil];
_fetchedResultsController.delegate = self;
}
Post
Replies
Boosts
Views
Activity
We now understand this issue much better. Working with Testers on devices using Japanese and Foreign languages and making some small changes to better handle Unicode we can reproduce the issue, but do not understand how to fix. We noticed the mismatch between the first character index on this screen shot attached and realized that our code to return the first character was not handling Unicode properly.
We therefore modified the code that returns the first Character of the Name Field and which produces the index of first letters running down the right side to be like so. This code is a method on our NSManagedObject that is the object used as the Entity in the DB for fetching. It has some debug Log statements.
This code crashes if we use the localizedUppercaseString and does not if we remove that?
// Used by NSFetchedResultController Table of Charts by first letter
-(NSString *)nameFirstLetter
{
if(self.name != nil) {
//return [[self.name substringToIndex:1] localizedUppercaseString];
NSString *str = [self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]];
NSLog(@"**** Name:[%@] 1st[%@] Upper[%@]", self.name, str, [str localizedUppercaseString]);
return [str localizedUppercaseString]; //[[self.name substringWithRange:[self.name rangeOfComposedCharacterSequenceAtIndex:0]] localizedUppercaseString];
}
else
return @"?";
}
It is crashing with a FetchRequest that is initialized using this code below. There is some issue created by doing the localizedUpperCase and the _sectionNameKeyPath that uses that returned value in the FetchRequest
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:_sectionNameKeyPath cacheName:nil];
-(void)initializeFetchedResultsController
{
// Already inititialized
if(_fetchedResultsController != nil) return;
// Create and configure a fetch request for Charts
NSManagedObjectContext *moc = ((iPhemerisAppDelegate *)[[UIApplication sharedApplication] delegate]).coreDataUtil.pc.viewContext;
if(!moc) {
NSLog(@"*** SavedChartView MOC NOT READY");
return;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Charts" inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *chartTypeDescriptor = [[NSSortDescriptor alloc] initWithKey:@"chartType" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, chartTypeDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_sectionNameKeyPath = @"nameFirstLetter";
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:_sectionNameKeyPath cacheName:nil];
_fetchedResultsController.delegate = self;
}
More information. We have managed to get Crashlytics logging to work and these are being sent with FireBase CrashReports. Thus far:
Crashing devices are NOT in low power mode.
Crashing devices are NOT getting any low memory notices.
Crashing devices ARE actually completing the CoreData / Cloudkit setup successfully as far as we can tell and notify the UITableView using CoreData to initialize the FetchResultController. At that point or after the UITableView viewDidLoad we attempt to make a FetchRequest.
It is at this point that some devices (only a small percentage) crash deep in CoreData framework with _computSectionInfo error, like so:
1 CoreFoundation 0x9908 isEqualToString + 100
2 CoreData 0xc0760 -[NSFetchedResultsController _computeSectionInfo:error:] + 712
3 CoreData 0x49c08 __43-[NSFetchedResultsController performFetch:]_block_invoke + 520
4 CoreData 0x82df8 developerSubmittedBlockToNSManagedObjectContextPerform + 156
5 CoreData 0x82948 -[NSManagedObjectContext performBlockAndWait:] + 208
6 CoreData 0x5fb24 -[NSFetchedResultsController _recursivePerformBlockAndWait:withContext:] + 152
7 CoreData 0x5da10 -[NSFetchedResultsController performFetch:] + 252
8 iPhemeris 0x1cf1c -[SavedChartViewController viewDidLoad] + 120 (SavedChartViewController.m:120)
Thanks Apple for the response, but from where I sit there have been many crashes not still no crash reports?!
@iAmBowser - We have never seen the crash on any of our devices or connected to Xcode. Only via reports and now users on TestFlight.
Yes it is in CoreData framework, we think it is a framework/Cloudkit issue and have filed a bug report. What the original question states is we are hoping to find a work around.
The issue only occurs with CloudKit enabled which CANNOT be run on simulators.
We doubt it is a database issue as this particular model has been working on many user devices and there are NO relationships, simply Entities.
We have tried 7 builds at this point via TestFlight attempting to use alternate approaches or more delayed approaches to initialize CoreData/Cloudkit. All the devices continue to crash in the same way and nothing we have tried prevents it accept the user turns off using iCloud.
We have tried to use Crashlytics to get log statements as to where it is crashing during start, but it seems to crash too quickly to even get those.
We are stuck
As of right now Mar. 22, 2023 NO TestFlight build that is crashing is delivering ANY crash reports. I am having to have users email them manually. What is going on APPLE?!
Issue STILL there in 16.3.1?! Does anyone have anymore information on this and how to resolve it or a workaround?! This is starting to be a real issue for me with bad reviews.
Issue STILL there in 16.3. APPLE!!!! Get the finger out on this one! This is causing hundreds of crashes and I get support emails every day about this crap. Also related are when the user uses a fetch with predicate and if the initial poster is correct will definitely change the section naming order. :(
I filed a bug report on this with all the crash logs we are getting in Organizer (FB11875507). And then submitted a TSR for help with a work around. Apples response was that there is no work around and it is there issue. Seeing a lot less crash reports (but still some with the same issue) on iOS 16.2 and now also some from beta users on 16.3, but the numbers are way down and I have been telling customers to upgrade to latest iOS. It helps most.
Yup! I am suddenly seeing a lot of crash reports (over 100 in the last couple of days) and ALL from users on iOS 16.1. This is new and nothing in that part of the App changed. Crashing right after the CoreData is initialized and NSPersistentCloudKitContainer is ready. At that point the controller does a fetchRequest for items in the Table and Crash with some kind of memory error.
In my case it is simply making a request for items sorted alphabetically?!
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!
***** THIS WAS THE REAL PROBLEM and SOLUTION *****
Moving stuff around helped me find a reliable way to produce the crash in Xcode and catch it in the debugger. Then I was able to consistently see way down in the crash report well after my code that each time it was mentioning an observer of NSUserDefaults. The class where all the above code was being used was registering itself as an observer of NSUserDefaults and also some Notifications. I was removing self as observer of NSNotifications but NOT NSUserDefaults. So after this class was released and attempted to persist values in NSUserDefaults that triggered a notification to it just as it was being release leading to -> CRASH!
Full Log
2022-11-15_14-28-39.6056_-0600-7ef0b8eca13f1281b1b0f3186ff46f5a59e728d2.crash
Full log now attached.
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.