Request with CoreData - cocoa

Here is my problem :
My CoreData model have two entity : Players and Results. The important properties of Players are :
- ratingTypePlayer, which is a String.
- resultsPlayer which is a Too-Many Relationship with Results as Destination. The Inverse relationship is playerResult.
I wish to obtain an array with all the Results of the Players which ratingTypePlayer is equal to "Toto". I don't find this kind of example.
Thanks a lot

You mean, like a fetch request?
NSManagedObjectContext *context = [[NSApp delegate] managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Player" inManagedObjectContext:context]];
[request setPredicate:[NSPredicate predicateWithFormat:#"ratingTypePlayer == %#", #"Toto"]];
NSArray *players = [context executeFetchRequest:request error:nil];
If you wanted an array of the "resultsPlayer" relationships, you could do:
NSArray *results = [players valueForKey:#"resultsPlayer"];

Related

iOS: Two Entities - Fetch, Merge, and Sort

I need to perform a fetch from two different entities, merge them, and then sort them based on a field that both entities have: 'lastModifiedDate'. lastModifiedDate is NSDate
NSFetchRequest *fetchRequest1 = [[NSFetchRequest alloc] init];
NSFetchRequest *fetchRequest2 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity1 = [NSEntityDescription entityForName:#"Entity1" inManagedObjectContext:self.managedObjectContext];
[fetchRequest1 setEntity:entity1];
NSEntityDescription *entity2 = [NSEntityDescription entityForName:#"Entity2" inManagedObjectContext:self.managedObjectContext];
[fetchRequest2 setEntity:entity2];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastModeifiedDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest1 setSortDescriptors:sortDescriptors];
[fetchRequest2 setSortDescriptors:sortDescriptors];
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
[self.refreshControl endRefreshing];
self.objects = results; //objects is an array #property
[self.tableView reloadData];
} onFailure:^(NSError *error) {
[self.refreshControl endRefreshing];
NSLog(#"An error %#, %#", error, [error userInfo]);
}
I'm stuck here. I need to merge fetchRequest1 and fetchRequest2, and have the objects display in a tableViewController in descending order. Thanks.
You can not merge two fetch requests, but you could merge and sort the fetched arrays:
NSArray *results1 = ...; // from first fetch request;
NSArray *results2 = ...; // from second fetch request;
NSMutableArray *merged = [[NSMutableArray alloc] init];
[merged addObjectsFromArray:results1];
[merged addObjectsFromArray:results2];
[merged sortUsingDescriptors:#[sortDescriptor]];
Alternatively, you could define an entity "Entity", make that the parent entity
for "Entity1" and "Entity2", and define all common properties (such as
"lastModifiedDate") in the parent entity. Then you can fetch and sort "Entity" objects.
(A possible disadvantage of entity inheritance is that Core Data uses a single table for all
"Entity", "Entity1" and "Entity2" objects. This is not optimal if there are many
properties that are not common to both.)

Core Data Returns incorrect value (although nearly right)

Okay so I ask the Core Data for a record (userKey), in that record is a PublicKey which I am extracting, however, publicKey ends up being 90% right but has a few extra characters at the beginning and is encapsulated in brackets.
I think my problem is I am getting a pointer rather than the data from the userMatches. Any guidance would be appreciated.
AppDelegate *appdelagate = [[UIApplication sharedApplication]delegate];
context = [appdelagate managedObjectContext];
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"KeyData" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
predicate = [NSPredicate predicateWithFormat:#"userKeyCD = %#", userKey];
[request setEntity:entitydesc];
[request setPredicate:predicate];
userMatches = [context executeFetchRequest:request error:&error];
NSString *publicKey = [userMatches valueForKey:#"publicKeyCD"];
Okay so I changed the last line to this and it worked fine;
NSString *publicKey = [[userMatches objectAtIndex:0] valueForKey:#"publicKeyCD"];
I did not show how it was defined but userMatches is an NSArray (sorry). It is used in a similar way further up the code;
NSArray *userMatches = [context executeFetchRequest:request error:&error];
Now I think about it is an array the right type?

Xcode - filter an NSFetchRequest and select each object

I am trying to filter a fetchRequest.
I'm at the point where the result is loaded into an NSArray.
However, I need to parse the array to pull out the individual items - right now, they look as if they were one object.
The code I'm using to get to this point is:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSManagedObjectContext *moc = coreDataController.mainThreadContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:moc];
[request setEntity:entity];
// Order the events by name.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[request setSortDescriptors:#[sortDescriptor]];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSArray *categories = [[moc executeFetchRequest:request error:&error] mutableCopy];
if (categories == nil) {
NSLog(#"bugger");
}
NSObject *value = nil;
value = [categories valueForKeyPath:#"name"];
This results as follows:
value = (
)
[DetailViewController loadPickerArray]
[AppDelegate loadPickerArray]
value = (
"Cat Two",
"Cat Three",
"Cat One",
"Cat Four"
)
Also, please note that the first time this ran, there were no results. I get that about 50% of the time.
Thanks for any help.
There are several methods you can filter your data.
The preferred way is to use a predicate for your search. This will give you the best performance.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSManagedObjectContext *moc = coreDataController.mainThreadContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:moc];
[request setEntity:entity];
// Order the events by name.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[CD] %#", #"Cat"]; //This will return all objects that contain 'cat' in their name property.
[request setPredicate:predicate];
[request setSortDescriptors:#[sortDescriptor]];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSArray *categories = [moc executeFetchRequest:request error:&error];
if (categories == nil) {
NSLog(#"bugger");
}
//Here you have the objects you want in categories.
for(Category *category in categories)
{
NSLog(#"Category name: %#", category.name);
}
If you wish to filter using an array, the following is possible also:
NSMutableArray *categories = [[moc executeFetchRequest:request error:&error] mutableCopy];
[categories filterUsingPredicate:[NSPredicate predicateWithFormat:[NSPredicate predicateWithFormat:#"name CONTAINS[CD] %#", #"Cat"]]
//Now, the only objects left in categories will be the ones with "cat" in their name property.
I recommend reading the Predicates Programming Guide, as predicates are very powerful, and it is much more efficient to filter your results in the store.

Cocoa Core Data: Setting default entity property values?

I know I can set default values either in the datamodel, or in the -awakeFromInsert method of the entity class. For example, to make a "date" property default to the current date:
- (void) awakeFromInsert
{
NSDate *now = [NSDate date];
self.date = now;
}
How though can I make an "idNumber" property default to one greater than the previous object's idNumber?
Thanks, Oli
EDIT: Relevant code for my attempt (now corrected)
- (void) awakeFromInsert
{
self.idNumber = [NSNumber numberWithInt:[self maxIdNumber] + 1];
}
-(int)maxIdNumber{
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Flight" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
// Set example predicate and sort orderings...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idNumber > %#", [NSNumber numberWithInt:0]];
[request setPredicate:predicate];
[request setFetchLimit:1];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"idNumber" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil | array.count == 0)
{
return 0;
}
return [[[array objectAtIndex:0] valueForKey:#"idNumber"] intValue];
}
If the maxIdNumber method is called, the new object is added to the table twice!? (but with the correct idNumber). The two entries in the table are linked - editing / removing one also edits / removes the other. For this reason I believe it has something to do with the managed object context. For what its worth, the outcome (two copies) is the same no matter how many times the maxIdNumber method is called in the awakFromNib; even if self.idNumber is just set to [NSNumber numberWithInt:5] and the maxIdNumber method is just called for a throwaway variable.
Any clues??
SOLVED IT!
Ok, the problem of double entry occurs when a fetch request is performed from within the awakeFromInsert method. Quoting from the docs:
You are typically discouraged from performing fetches within an implementation of awakeFromInsert. Although it is allowed, execution of the fetch request can trigger the sending of internal Core Data notifications which may have unwanted side-effects. For example, on Mac OS X, an instance of NSArrayController may end up inserting a new object into its content array twice.
A way to get around it is to use the perfromSelector:withObject:afterDelay method as outlined here (I am only allowed to post one hyperlink :( ):http://www.cocoabuilder.com/archive/cocoa/232606-auto-incrementing-integer-attribute-in-awakefrominsert.html.
My working code is now as follows: (note, I have put the bulk of the fetching code used above into a category to tidy it up a little, this allows me to use the method fetchObjectsForEntityName:withPredicate:withFetchLimit:withSortDescriptors:)
- (void) awakeFromInsert
{
[self performSelector:#selector(setIdNumber) withObject:nil afterDelay:0];
self.date = [NSDate date];
}
-(void)setIdNumber
{
int num = 0;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"idNumber" ascending:NO];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idNumber > %#", [NSNumber numberWithInt:0]];
NSArray *array = [[self managedObjectContext] fetchObjectsForEntityName:#"Flight"
withPredicate:predicate
withFetchLimit:0
withSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
if (array != nil & array.count != 0)
{
num = [[[array objectAtIndex:0] valueForKey:#"idNumber"] intValue];
}
num ++;
[self setIdNumber:[NSNumber numberWithInt:num]];
}
Let me know what you think!
One Approach: Create a fetch request of all instances of your entity with a limit of 1, sorted by idNumber to get the highest number.
Another Approach: Keep the highest idNumber in your store's metadata and keep incrementing it.
There are plenty of arguments for and against either. Ultimately, those are the two most common and the choice is yours.
An easier way to do that is to override the newObject method of NSArrayController:
- (id) newObject
{
id result=[super newObject];
[result setValue: [NSDate date] forKey: #"date"];
return result;
}

Display data from NSArray in NSTableView

I have a problem, and I reckon there's a really straightforward solution to it, but I can't fathom it out!
I have this piece of code:
NSManagedObjectContext * context = [[NSApp delegate] managedObjectContext];
NSManagedObjectModel * model = [[NSApp delegate] managedObjectModel];
NSDictionary * entities = [model entitiesByName];
NSEntityDescription * entity = [entities valueForKey:#"Post"];
NSPredicate * predicate;
predicate = [NSPredicate predicateWithFormat:#"date < %#", [NSDate date]];
NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObject: sort];
NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
[fetch setEntity: entity];
[fetch setPredicate: predicate];
[fetch setSortDescriptors: sortDescriptors];
NSArray * results = [context executeFetchRequest:fetch error:nil];
[sort release];
[fetch release];
This returns the specified data in an Array. I now want to display this data in a NSTableView.
How would I go about doing this?
Thanks!
You could use it as the data for an NSArrayController which you bind to the NSTableView.
Looks like you need to read into bindings.

Resources