Crash on accessing NSManagedObject property after migratePersistentStore: to another URL on OS X 10.9 Mavericks - macos

I have a code that works fine on OS X 10.9 before I migrate the persistent store to another URL. After migration app crashes when I try to execute fetch request and sort by an existing property of fetched items. On OS X 10.8 it works fine before and after migration. Any help is highly appreciated.
Here's the store migration, nothing fancy:
BOOL success = [storeCoordinator migratePersistentStore:store toURL:[NSURL fileURLWithPath:newPath] options:nil withType:NSSQLiteStoreType error:&error];
Migration proceeds successfully, without errors.
Here's the crashing code (which worked fine before migration):
NSManagedObjectContext *moc = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:moc];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == nil", #"parent"];
[request setPredicate:predicate];
// SORTING BY "sortOrder" PROPERTY CRASHES THE APP
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error;
NSMutableArray *fetchResults = [moc executeFetchRequest:request error:&error];
Crash details:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x60)
0x00007fff89a64ad0 in _propertyAtIndexForEntityDescription ()
0x00007fff89a631dd in snapshot_get_value_as_object ()
0x00007fff931ea49f in -[NSObject(NSKeyValueCoding) valueForKeyPath:] ()
0x00007fff931e904c in _sortedObjectsUsingDescriptors ()
0x00007fff931e8e69 in -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] ()
0x00007fff89a459f0 in -[NSManagedObjectContext executeFetchRequest:error:] ()
Update [2]
Thanks to Tom Harrington's question, I was able to narrow the issue down to this:
Update [3]
It appears there's a number of specific keys causing this crash. Out of dozen of different properties of the fetched items, accessing 3 particular properties of type NSDate and 'double' cause the app to crash. All of them has some meaningful value.
There're other properties of type NSString, Boolean, Integer 16, Integer 32 and Binary Data. Some of them are nil, some set to default value, some has some other value. They can be accessed without any error. AND there's one NSDate property without any value including default and it can be accessed without problems also.
Why does it crash? Before migrating store to another URL I can successfully access these properties.

Seems to be a problem with a specific project created a while ago. Probably during these years something got messed in project files and settings.

Related

Core data predicates with key-paths

So i'm trying to work out how to use these predicates, i've read the Apple doc and am trying to use it (https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html) and i have the predicate set up, but it keep getting Thread 1: EXC_BAD_ACCESS (code =) etc.etc.
NSError *error;
NSLog(#"1");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Fruit" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSLog(#"2");
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"Source.sourceName contains[cd] %#", "Apple Tree"];
[fetchRequest setPredicate:predicate];
NSLog(#"3");
NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"4");
testLbl.text = [fetchResult objectAtIndex:0];
Thats the code i'm using, as for the Core Data we have...
Entities Fruit & Source
Attributes fruitName & sourceName
Relationship (one to one) fruitSource<--------->sourceFruit
What i want to do is pull out any fruit that comes from an Apple Tree... >.<
There are two different problems:
To get from Fruit to the related Source you have to use the relationship: #"fruitSource.sourceName contains ..." instead of #"Source.sourceName contains ...".
(This is probably causing the exception.) The %# format requires an Objective-C object as argument, not a C string: #"Apple Tree" instead of "Apple Tree".
So the predicate should look like this:
[NSPredicate predicateWithFormat:#"fruitSource.sourceName CONTAINS[cd] %#", #"Apple Tree"]

How to calculate values that are in an specific time range in cocoa touch

i need some help for getting my statics work...
I use core data to store my values from user input and each "line" has a time value.
Now i need to calculate some of these values that are in specific time range, lets say the last 30 days.
But i don't know how to do it, i'm a little new to working with date and time rangers.
Can somebody help me out?
kind regards,
Ingemar
You need to use a predicate to filter your data.
NSManagedObjectContext *context; // Assume this exists.
NSEntityDescription *entityDescription; // Assume this exists.
NSDate minDate, maxDate; // Assume these exist.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSPredicate *setPredicate:predicate = [NSPredicate predicateWithFormat:#"date BETWEEN %#",
[NSArray arrayWithObjects:minDate, maxDate, nil]];
[request setPredicate:predicate];
NSError *error;
NSArray *filteredResult = [context executeFetchRequest:request error:&error];
// Handle error.
[request release];
thanks for your answer. I will try your way.
I found this solution by myself, any concerns about it?
NSTimeInterval aktuellesDatumInSekunden = [aktuellesDatum timeIntervalSinceReferenceDate];
NSTimeInterval vordreissigTagen = [letztedreizigTage timeIntervalSinceReferenceDate];
double dBoluse = 0;
double dWerteKleinerSechzig = 0;
for (TblBolusWerte *ausgabeBoliTag in statistikDataWithPredicate){
NSTimeInterval DatumAusDB = [ausgabeBoliTag.creationTime timeIntervalSinceReferenceDate];
if (DatumAusDB >= vordreissigTagen && DatumAusDB <= aktuellesDatumInSekunden){

better way to find max date inside big pool of core data objects

i have big pool of core date objects (around 10000) and there is too long time doing code according profile:
NSDate *maxExternalChangedDate = [codes valueForKeyPath:#"#max.externalChangedDate"];
is community know better way to found it?
NSString *rateSheetID = [rateSheetAndPrefix valueForKey:#"rateSheetID"];
NSFetchRequest *requestCodesForSale = [[NSFetchRequest alloc] init];
[requestCodesForSale setEntity:[NSEntityDescription entityForName:#"CodesvsDestinationsList"
inManagedObjectContext:self.moc]];
[requestCodesForSale setPredicate:[NSPredicate predicateWithFormat:#"(%K.carrier.GUID == %#)",relationshipName,carrierGUID]];
NSError *error = nil;
NSArray *codes = [self.moc executeFetchRequest:requestCodesForSale error:&error];
if (error) NSLog(#"Failed to executeFetchRequest to data store: %# in function:%#", [error localizedDescription],NSStringFromSelector(_cmd));
NSNumber *count = [codes valueForKeyPath:#"#count.externalChangedDate"];
if (count == 0) {
[requestCodesForSale release];
[pool drain], pool = nil;
return YES;
}
NSDate *maxExternalChangedDate = [codes valueForKeyPath:#"#max.externalChangedDate"];
By using NSFetchRequest and returning NSDictionaryResultType You can use NSExpressionDescription to yeild the results for functions like max() and min().
Sample Code from Apple
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"salary"];
NSExpression *maxSalaryExpression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxSalary"];
[expressionDescription setExpression:maxSalaryExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
Check out this doc for more information.
Core Data Programming Guide
I have the same issue.
In theory this should work, but for me it did not.
For some reasons the query crashes with the error that the database is corrupt.
In the end I perfomed a query, where I ordered on my field DESCENDING, and using setFetchLim it:1. Its not perfect, but at least it worked.
Also I made sure the field I use has an index.
Since I have few records, this works fine.
On 30000 records, it might be a problem though.
I followed the IOS documentation, fot fatch a "max:" query, but only got "database corrupted" errors. That is the sample code from Apple fails BADLY.
Googling the internet, it seem the call to setPropertiesToFetch fails in IOS 5+ ??!
I have not found any way around that.
Using a normal query, it worked without any issue.
So I must conclude Apple code is no longer corerct.

Reading a plist

I'm trying to read ~/Library/Preferences/com.apple.mail.plist (on Snow Leopard) to get the email address and other information to enter into the about dialog. I'm using the following code, which is obviously wrong:
NSBundle* bundle;
bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"~/Library/Preferences/com.apple.mail.plist" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSString *item = [plistData valueForKeyPath:#"MailAccounts.Item 2.AccountName"];
NSLog(#"Result = %#", item);
Moreover, the value I need to read is MailAcounts -> Item 2 -> AccountName and I am not sure I am doing this correctly (due to the space in the Item 2 key).
I tried reading Apple's developer guide to plist files but no help there.
How can I read a plist and extract the values as an NSString?
Thanks.
The first level is an array, so you need to use "MailAccounts.AccountName" and treat it as NSArray*:
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *item = [plistData valueForKeyPath:#"MailAccounts.AccountName"];
NSLog(#"Account: %#", [item objectAtIndex:2]);
Alternatively you can go by keys and pull the array from "MailAccounts" first using valueForKey: (which will yield NSArray*) and then objectAtIndex: to get the dictionary of that particular account (useful if you need more than the name).
Two things:
You don't want or need to use NSBundle to get the path to the file. The file lies outside of the app bundle. So you should just have
NSString *plistPath = #"~/Library/Preferences/com.apple.mail.plist";
You have to expand the tilde in the path to the user directory. NSString has a method for this. Use something like
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];

Setting up relationships while importing core data?

I have an in memory managed object context called importMoc that I use to import records (e.g. employees). I have parsed a file and set up the employee objects in importMoc with one very important exception. The user confirms they want to process %d employees but I can't quite figure out how or when to set the "parent" relationship of the employees (e.g. setting their department).
For my purposes they will all be imported into the same department (which the user has already implicitly selected).
Obviously I can't set up the relationships across the two contexts so do I:
Create a department in importMoc and then when I merge changes merge the "import" department with the "real" department?
2) Merge the employees and then fetch all the freshly imported employees (somehow!!!) and set their department then?
3) Some other solution that I have overlooked?
It seems like a simple problem but for some reason (laziness? tiredness? stupidity?) I can't figure out how to go about it! Everything I've tried so far seems far too elaborate and complicated!
Thanks in advance!
If the Department objects have already been saved to a persistent store, then you can bring them into another managed object context. Since your objects will all have to live in the same persistent store anyway (as cross-store relationships are not allowed), you should be able to simply fetch the ones you need into importMoc.
For example:
foreach (NSDictionary *record in employeeRecords) {
NSManagedObject *employee = [NSEntityDescription insertNewObjectForEntityForName:#"Employee" inManagedObjectContext:importMoc];
// Configure employee however you do that
NSString *managerID = [record objectForKey:#"someKeyThatUniquelyIdentifiesTheManager"];
NSFetchRequest *managerFetchRequest = [[NSFetchRequest alloc] init];
[managerFetchRequest setEntity:[NSEntity entityForName:#"Manager" inManagedObjectContext:importMoc]];
[managerFetchRequest setPredicate:[NSPredicate predicateWithFormat:#"managerProperty == %#", managerID]];
NSArray *managers = [importMoc executeFetchRequest:managerFetchRequest error:nil]; // Don't be stupid like me and catch the error ;-)
[managerFetchRequest release];
if ([managers count] != 1) {
// You probably have problems if this happens
}
[employee setValue:[managers objectAtIndex:0] forKey:#"manager"];
}
You could also just do a single fetch request to get all of the managers into importMoc and then filter that array to locate the right one each time. That would probably be a lot more efficient. In other words, don't do what I just told you to do above :-)
1/ assume employee record X has name Y and department id 15 (i.e. it refers the department with id 15 via a relation)
2/ load department with department id 15 from the managed object context
3/ create an employee that refers the object created at (2)
i.e. (note: sample code only, probably won't compile):
NSEntityDescription * departmentED = [NSEntityDescription entityForName:#"Department" inManagedObjectContext:moc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(id == %#)", deptId];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:departmentED];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
/* create employee */
NSEntityDescription * employeeED = [NSEntityDescription entityForName:#"Employee" inManagedObjectContext:moc];
NSManagedObject * employeeMO = [[[NSManagedObject alloc] initWithEntity:employeeED insertIntoManagedObjectContext:moc] autorelease];
/* set employee department to department instance loaded above */
[receiptObject setValue:[array objectAtIndex:0] forKey:#"department"];

Resources