Fetch update in Cocoa - cocoa

this is the code in my AppDelegate.m:
-(IBAction)fetch:(id)sender{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Foo" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == 'some title'"];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {NSLog(#"Error: %#", error);}
NSMutableArray *fooArray = [[NSMutableArray alloc]init];
for (Foo *f in fetchedObjects) {
//here another fastenum for a to-many relationship
for(Bar *b in f.relationship){
[fooArray addObject:b.title];
}
}
Everytime I perform the fetch action, even if I've changed the app.storedata file via UI and checked the changes in finder, the result is always the same until i quit the application. After a restart, the fetch result is up to date and aligned with the app.storedata file. The fooArray count is always the same, regardless if I add some entries in the entities and coredata save everything.
I've tried with [fetchRequest setIncludesPendingChanges:YES] but it doesn't affect the behaviour.
How to update the fetch result while the app is running?
UPDATE: i've "solved" the problem with this workaround:
-(IBACTION)fetch:(id)sender{
_managedObjectContext = nil;
_persistenStoreCoordinator = nil;
//rest of the code...
Is this workaround a final solution? Is there a more "correct" way to solve this problem?

Related

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.

How to solve: Why is coreData(NSFetchedResultsController) sorting differently in iOS5 and iOS4

i have a tableview which is populated with NSFetchedResultsController.
On didSelectRowAtIndexPathi am updating the current row. The sorting here should not change. The difference in the selected row is, that i mark it as read. No title changes or something.
In iOS4 the row gets updated and still leave on his current place, it does not move to the end of section. Thats the right way.
in iOS5 the row gets also updated but it moves to the end of the Section. I want to prevent that. How can i do that? And why is that happening?
See my code:
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil)
{
return __fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"EntitySetsCardsInbox" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *inboxPred = [NSPredicate predicateWithFormat:#"archived == 0 AND cardId != 0"];
[fetchRequest setPredicate:inboxPred];
if (sortString == nil) {
//sortString = [[NSString alloc] initWithString:#"sortingOrder"];
sortString = [[NSString alloc] initWithString:#"colorOrder"];
sortAsc = NO;
}
if ([sortString isEqualToString:#"cardTitle"]) {
NSSortDescriptor *sortDescriptor2 = [[[NSSortDescriptor alloc] initWithKey:sortString ascending:sortAsc selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor2, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
XLog(#"sortDescriptors: %#", sortDescriptors);
} else {
NSSortDescriptor *sortDescriptor2 = [[[NSSortDescriptor alloc] initWithKey:sortString ascending:sortAsc] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor2, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
XLog(#"sortDescriptors: %#", sortDescriptors);
}
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[...]
}
i think your sortdescriptor is changing when you updating your rows. add one more sortdescriptor to fix the list with it and check it again. in my case i have a very similar issue and it solved my problem. maybe yours, too.

Core Data fetchedResultsController errors 'A fetch request must have an entity' entityForName returns nil

Hi I set up my own coredata app, or I tried...
First I created the xdatamodel and generated the Modelclasses, after this I implemented all the function of core-data in AppDelegate which I found in a generated project. Finally I copied the fetchedResultsController in my TableViewController.
fetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ParameterGroup" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
First I checked if the managedObjectsController is != nil, it has a address
Then I copied the EntityName from my xdatamodel in entityForName,
but NSEntityDescricption entity is nil.
And if I just create a new object the exception says, that the entity doesn't exist
Do I have to connect the xdatamodel to my project?
Hope you can help me
Thanks a lot!!!
The most common cause of this problem is simply misspelling the entity name wrong in the code such that it doesn't match the entity name in the data model.
Copy and paste the entity name from the model to the code and see if that fixes the problem.
The simplest way to solve this, given that you haven't done a lot coding on non-core-data parts, is probably to create a new project where you check the box for "Use Core Data". If you're going to use a Navigation Bar, choose this as your template. If I recall correctly, this will generate a table view with all functions needed. You'll have to modify the datamodel (generated).
Remark that you'll have to delete the app from the Simulator if it is installed and you change the datamodel (otherwise the generated data will not be consistent with the datamodel and the app will crash)

How to optimize a UILocalNotification process

I'm trying to send multiple localNofications using a fetch request on an entity
And though this code works fine
NSFetchRequest *myRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"active == YES"];
[myRequest setEntity:[NSEntityDescription entityForName:#"Entry" inManagedObjectContext:managedObjectContext]];
[myRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest: myRequest error: &error];
if (fetchedObjects == nil){
// Deal with error...
}
// We fill the NSMutableArray with the values of the fetch
self.activeList = [[NSMutableArray alloc] initWithArray:[fetchedObjects valueForKey:#"textbody"]];
[self scheduleAlarms:[self.activeList objectAtIndex:0]];
[fetchedObjects release]; //this line crashes the app
1) if I release fetchedObjects, the app crashes. Aren't I supposed to release it ?
2) Could I use the localNotif.userinfo to optimize the code instead of calling a method to schedule each localNotification with the strings in my activeList ? I can't figure out how to do it.
Thanks,
Mike
1) executeFetchRequest returns an autoreleased NSArray, you don't need to release it manually
2) not clear what do you want to optimize...

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;
}

Resources