How to get all entries of a NSFetchedResultsController/NSManagedObjectContext? - iphone

I have a nice working iphone app that works with core data. I use a NSFetchedResultsController/NSManagedObjectContext as described in various tutorials.
Now I want to extends my app and add some more features. The problem I need to build up an array with objects that have informations from my data.
I somehow need to get a list of all data I have in my context.
I thought I could make an approach similar to the way I get the data for the UITableView.
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
This one fails, because I do have actually multiple sections. Now I could go through all sections and make my own IndexPath to access my data with :
MyData *info = [_fetchedResultsController objectAtIndexPath:indexPath];
But I think there is another way I just have not found yet and I hope someone can help me out here.
Thanks a lot.

are you just looking for a method to get all objects from you NSFetchedResultsController? If so, use this.
NSArray *fetchedData = [_fetchedResultsController fetchedObjects];
if you have more than 1 entity build a fetchrequest for each entity. Something like this should give you all your objects.
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSError *error;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];

If you wanted to get all of the objects for a particular section you could do this:
NSArray *sectionObjects = [_fetchedResultsController.sections[section_number] objects];

Related

Fetching strings (or other variables) from a CoreData fetch request?

I have Core Data setup in my app and need to fetch a bunch of items and then access the properties I choose of those fetched items. I am able to successfully fetch a bunch of results like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TableInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
This gives me an array with my results, great. Now from this how can I for example get the 'name' property from these results? In this specific circumstance I want to load an array with all of the fetched results 'name' strings.
If I read your question correctly, you are able to fetch your NSManagedObjects without difficulty, but would like to derive another NSArray with name properties on those managed objects?
Then you can use the valueForKeyPath on the NSArray (extending your original code):
NSArray *names = [result valueForKeyPath:#"name"];
You can use the key-value:
for (NSManagedObject *fetchedResult in result) {
NSLog(#"name = %#", [fetchedResult valueForKey:#"name"]);
}
or if you created your custom NSManagedObject:
for (EntityObject *fetchedResult in result) {
NSLog(#"name = %#", [fetchedResult name]);
}

Populating the Tableview from the Core Data

I have performed the save operation in core data and it is successfully done .It stores the data .I have also fetched the data into the log. This is my code for fetching data in log but I dont know how to fetch this data in TableView.
NSError *error;
DemoAppCoreDataAppDelegate *appdelegate = (DemoAppCoreDataAppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appdelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName: #"Employee" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedobject = [context executeFetchRequest:fetchRequest error:&error];
tablearray = [[NSMutableArray alloc] initWithArray:fetchedobject copyItems:YES];
for (NSManagedObject *info in fetchedobject ) {
NSLog(# "%#",[info valueForKey:#"name"] );
}
[fetchRequest release];
I would recommend to use a NSFetchedResultsController.
Apple provides complete sample code in the NSFetchedResultsController documentation
The NSFetchedResultsController is specifically designed to work in between a tableView and Core Data. It makes everything a lot easier.
For example it will automatically insert and delete rows when you add or remove objects from the core data. For this you have to implement the NSFetchedResultsControllerDelegate protocol. The full sample code for this is in the protocol documentation
Use fetchedobject as dataSource to the tableView.
After [fetchRequest release]; , set the delegate and datasource for the tableView programatically.
The best way in this case is to inspect some working example I think. Look at the apple's example project Recipies, good one btw: easy and gives a good picture of how the things with CoreData and tableViews should be done:
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008913

Saving Core Data From Multiple TableVIews [duplicate]

This question already has answers here:
Querying Core Data with Predicates - iPhone
(4 answers)
Closed 2 years ago.
I have an app that has multiple tableviews and I want to use Core Data to capture all the data. I have two entities - freezers and items. In the first tableview I add a freezer and it saves correctly. I quit the app, re-open, and it is there. I click on the freezer (opening another tableview) and add some items and I can see them in my new sectioned tableview. I quit my app, restart it, see the freezer, click on it and there are no items.
I have my managedObjectContext in my appDelegate and reference it from there using all views, so I am not creating multiple instances. Here is the code I use to save the items to a freezer, both the managedObjectContext and my itemsArray:
Item *item = (Item *)[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:[delegate managedObjectContext]];
[item setFreezer:freezerName];
[item setName:name];
[item setQuantity:quantity];
[item setSection:section];
[item setAdded:added];
[item setNotes:notes];
NSError *error = nil;
if (![[delegate managedObjectContext] save:&error]) {
NSLog(#"Freezer info didn't save. Need to handle this.");
}
[items insertObject:item atIndex:0];
Here is the code I use in the ItemViewController to retrieve the items within viewDidLoad:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"freezer == '%#'", freezerName];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:[delegate managedObjectContext]];
NSSortDescriptor *sorts = [[NSSortDescriptor alloc] initWithKey:#"section" ascending:NO];
NSArray *sort = [[NSArray alloc] initWithObjects:sorts, nil];
[request setSortDescriptors:sort];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray *results = [[[delegate managedObjectContext] executeFetchRequest:request error:&error] mutableCopy];
if(results == nil) {
NSLog(#"Error fetching results... need to handle");
}
[self setItems:results];
NSLog(#"items count:%d", [items count]);
The item count returned is zero.
I am completely stumped and have spent several hours searching online, trying different things, and I can't figure it out. I know there are some much smarter coders out there and I hope one of you can see what the problem is.
I would try to change the predicate to
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"freezer like %#", freezerName];
Hope that helps!
Maybe the problem is that you load data in
- viewDidLoad:
method. It's called only once when your view is loaded, so when underlaying data get's changed, your view controller is not aware about it.
You can either move your loading code to
- viewWillAppear:
method or introduce notifications to spread the information that data store has changed its state and reload table views upon that event.
The best way is probably to use NSFetchedResultsController as your data source, as its always aware of its data store changes. Check docs for reference to that class.

How to Determine if a Table Contains Any Records with Core Data?

I haven't seen any other questions quite like this on here, but I'm hoping someone has some insight. I'm just starting to learn Core Data.
Basically, I have two methods and I want to choose which one to call with an if/else statement based on whether or not the "Contacts" table contains any records. Is there a way using core data to check if there are any records in a table?
The best way I've found so far is to set the fetchLimit to 1 and then check to see if anything returns.
[request setFetchLimit:1];
But I keep thinking there has to be a better/easier way. Anyone know or have a good reference I can look at?
Thanks a ton!
Yes, definitely there is a better method. Setup a fetch request as usual, but, instead of actually executing it, simply ask for the number of objects it would have returned if it had been passed to executeFetchRequest:error:
This can be done using
- (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
Something like this:
- (int) numberOfContacts{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSManagedObjectContext *managedObjectContext = yourManagedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];
[request release];
if (!error){
return count;
}
else
return -1;
}
It's not necessarily any better or easier, but you can look for a specific record and then create it if it doesn't exist like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSError *error;
// Filter based on a predicate
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"identifier == %#", #"1"]];
NSManagedObject *contact = [[managedObjectContext
executeFetchRequest:fetchRequest error:&error] lastObject];
// If the contact was not found
if (!contact)
{
// Create the contact
contact = [NSEntityDescription insertNewObjectForEntityForName:#"Contact"
inManagedObjectContext:managedObjectContext];
[contact setValue:[NSNumber numberWithInt:1] forKey:#"identifier"];
[managedObjectContext save:nil];
}
Marcus Zarra posted some code that demonstrates this in a feed reader app. Marcus is the Core Data master.

Core Data : How to check for the presence of Many to Many relationship

I have a "Song" Entity and a "Tag" entity and they have a many to many relationship between them. A Song can have multiple Tags and a Tag can be applied to multiple Songs.
I want to check if a Song has a particular Tag associated with it. If the Song has the Tag associted with it, I want to show a checkmark in the table view.
For a similar logic, in Apple "TaggedLocations" sample code, the following check is made to check for the presence of the relationship.
if ([event.tags containsObject:tag]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
This may be inefficient if there are a lot of Tags in the database as this will fetch all of them in the memory. Please correct me if I am wrong here.
Is there a more efficient way to check if the Song is associated with a particular Tag instead of checking in Song.Tags?
It's actually pretty easy to do, if completely undocumented. You want to create a fetch request with a predicate that has a set operation. If we imagine that your Tag model has a property called tagValue, the predicate you care about is "ANY tags.tagValue == 'footag'"
NSString *tagSearch = #"footag";
// However you get your NSManagedObjectContext. If you use template code, it's from
// the UIApplicationDelegate
NSManagedObjectContext *context = [delegate managedObjectContext];
// Is there no shortcut for this? Maybe not, seems to be per context...
NSEntityDescription *songEntity = [NSEntityDescription entityForName:#"Song" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:songEntity];
// The request looks for this a group with the supplied name
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.tagValue == %#", tagSearch];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
[request release];
You are correct, using that code will retrieve the entire set and the object comparison may be quite complex, depending on how many properties and relationship are part of the object's entity.
Anyway, you can not avoid a set comparison for inclusion. Probably, the best you can do is to avoid fetching all of the properties/relationships by asking Core Data to retrieve NSManagedObjectID Objects only.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Tag" inManagedObjectContext:[self managedObjectContext]]];
[fetchRequest setResultType:NSManagedObjectIDResultType];
NSManagedObjectID objects are guaranteed to be unique, therefore you can safely use them to check for set inclusion. This should be much more efficient for a performance perspective.