Is this the correct use of NSFetchedResultsController? - objective-c

Is this is correct us of NSFetchedResultsController's getter method with the most modern Object-C and LLVM compiler that doesn't require synthesizing properties?
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
if (!self.managedObjectContext) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate: self.predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController= [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return aFetchedResultsController;
}
In other parts of my code, I have this instead:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Set" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat: #"sets == %#", self.exercise]];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return self.fetchedResultsController;
}

In your first example, you never assign anything to the property or ivar, so the lazy loading will occur every time the property is accessed. This isn't right.
In your second example you assign to the property, (thereby triggering any side effects of the property setter) and also return the value of the property (which wouldn't take many errors to leave you with an infinite loop). It may work now but it would be easy to break with a careless update.
It would be cleaner to assign to the internal ivar and return that.
So, at the end of your first example:
_fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;

Related

Core Data crash: Keypath Project not found in entity

- (void)fetchResult
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"Project" ascending:YES];
[request setEntity:self.entityDescription];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
self.fetchResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
NSError *fetchError = nil;
BOOL success = [self.fetchResultController performFetch:&fetchError];
if (success) {
NSLog(#"fetched!");
}
else {
NSLog(#"fetch fail!");
}
}
The entityDescription and the context is set in the viewDidLoad method, as it is shown below:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
self.entityDescription = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:self.managedObjectContext];
Crash info:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath Project not found in entity '
Could anybody give a hand?;)
The key used in the sort descriptor
[NSSortDescriptor sortDescriptorWithKey:#"Project" ascending:YES];
must be a property of your Core Data entity, not the name of the entity itself.
So you should replace #"Project" in the sort descriptor by some property of
the Project entity, such as #"name".
It is not related to the initial question, but it is highly related to the title of the question.
And as it was the only one I've found for this topic, it may be useful for someone else:
So, I've got "Core Data crash: 'keypath Videos not found in entity ...'" because I've used string interpolation in a predicate:
let projectName = "Videos"
let predicate = NSPredicate(format: "\(#keyPath(Scene.relatedProject.name)) == \(projectName)")
The correct way is:
let projectName = "Videos"
let predicate = NSPredicate(format: "\(#keyPath(Scene.relatedProject.name)) == %#", projectName)
Hope, it will save someone hour(s) of figuring out what is going on.
make sure you are using below syntax for your Query
Example:
fetchRequest.predicate = NSPredicate(format: "username = %#", userName)
where username is the key and userName is variable to hold any name in the example.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setPredicate:predicate];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"Column_Name" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *arrTemp = [context executeFetchRequest:fetchRequest error:&error];
if([arrTemp count]>0){
NSLog(#"%#",arrTemp);
}
Set cache name like:
self.fetchResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"temp"];

NSFetchedResultsController for new object

I have UITableViewController with static editable cells. In my UITableViewController I make CoreData object, like this:
- (User*)user {
if (_user == nil) {
_user = [NSEntityDescription insertNewObjectForEntityForName:TABLE_USER inManagedObjectContext:self.managedObjectContext];
NSError* error;
[self.managedObjectContext save:&error];
if (error != nil) {
abort();
}
}
return _user;
}
I want to get the object in NSFetchedResultsController that would display its fields in a table, and then save the object.
I'm trying to do:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:TABLE_USER_PROFILE inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
NSPredicate* pred = [NSPredicate predicateWithFormat:#"%# = %#", TABLE_USER, self.user];
[fetchRequest setPredicate:pred];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:TABLE_USER];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
abort();
}
return _fetchedResultsController;
}
But it didn't work. I get empty fetchedResultsController.
If self.user is a NSManagedObject and you query against a User entity you could try to edit your predicate like the following:
NSPredicate* pred = [NSPredicate predicateWithFormat:#"self == %#", self.user];
Hope that helps.
P.S. If my answer doesn't work try to provide some other details (see my comment)

iOS: How to Sort with NSSortDescriptor a value from relationship?

I have two objects with a relationship in my Data Model...
Person {Name, position, Title}
Position {position, PositionSort}
Each of the above have their own class files (subclasses of NSManagedObject). I'm trying to sort by PositionSort above. I can currently sort by Position by using the following code.
NSSortDescriptor *sortDescriptorPosition = [[NSSortDescriptor alloc] initWithKey:#"position" ascending:YES];
I am unsure though how I'm able to access and use the PositionSort. Any ideas? Thanks!
UPDATE: Added full function...
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptorState = [[NSSortDescriptor alloc] initWithKey:#"position.positionSort" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptorState, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"(companyID == %#)", company.companyID];
[fetchRequest setPredicate:searchPredicate];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"position.positionDescription" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
With the above code I'm getting the following error at Runtime.
2012-08-12 10:29:45.499 College[269:707] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xd6bd310> valueForUndefinedKey:]: this class is not key value coding-compliant for the key positionDescription.'
I'm not sure you want to sort exactly, but the following NSSortDescriptor can be used to sort the Person objects using the PositionSort value.
NSSortDescriptor *sortDescriptorPosition = [[NSSortDescriptor alloc] initWithKey:#"position.PositionSort" ascending:YES];
perhaps, you looked for this...

Detect updates on sectionNameKeyPath from NSFetchedResultsController

Here is the case. I have 2 models:
Sport (with attribute name and relationShip players)
Player (with attribute name and relationShip sport)
In a ViewController, I wan't to display all players in a UITableView ordered by sport.
When a player name changes, NSFetchedResultsControllerDelegate callbacks are called.
But when the name of a Sport is updated, no NSFetchedResultsControllerDelegate callbacks are called.
Is this a normal behaviour ? How can I know the updates of Sport.name (without creating another NSFetchedResultsController) ?
Thank you and Best Regards.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sport.name != NULL"];
[fetchRequest setPredicate:predicate];
NSArray *sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"sport.name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDescription];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sport.name"
cacheName:nil];
[fetchRequest release];
fetchedResultsController.delegate = self;
NSError *error = nil;
if ([fetchedResultsController performFetch:&error] == NO) {
ALog(#"Fetch error: %#", [error localizedDescription]);
}
You can using a tip:
If (fetchresultcontroller != nil || isOtherEnthityUpdate)
{
return fetchresultcontroller;
}
And do self.tableview reload from any place where u check other entity. It can be notification center or other tableview.

How to load all instances of entity / property?

i have some Data in a TableView (loaded from coreData) and now i want to upload this data to a webserver.
Before i can do that i want to save all data to a *.txt file.
How can i load all instances of an entity / property to an mutable array?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"barCode" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
Thanks,
brush51
Edit 1:
Can you give me a little Help (i dont want ALL data, just the property "barCode"), now i have this code:
NSManagedObjectContext *moc = [self managedObjectContext];
NSFetchRequest *req = [[NSFetchRequest alloc] init];
[req setEntity:[NSEntityDescription entityForName:#"Event" inManagedObjectContext:moc]];
[req setIncludesPropertyValues:NO];
NSError *error;
NSArray *codes = [moc executeFetchRequest:req error:&error];
[req release];
for (NSManagedObject *Event in codes) {
NSLog(#"there it is : ------------> %#", codes);
NSLog(#"barcodes ------------------> %#", [[managedObject valueForKey:#"barCode"] description]);
}
How to output ONLY the instances of the property "barCode" ??
thanks for answering,
here is my code:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"barCode" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
I have no predicate... Predicate is for what?
If you do a fetch request on the entity without a predicate, you'll get all the instances.