NSFetchedResultsController and child UITableViewController - iphone

Right now I have an UITableViewController that displays a set of Artist objects sorted into sections (alphabetically) using NSFetchedResultsController. When you tap an artist in that list, a second UITableViewController is pushed and displays the artist.shows objects, also sorted into sections (by date).
Right now I'm sorting that second data source (artist.shows) "manually", using NSSortDescriptor and a for() loop to determine where to have the table view sections (one section for every month).
Is that the right way to go? Would it make more sense to create a second NSFetchedResultsController to sort that data, although it would basically be fetching data I already have in artist.shows?

You should definitely use a second fetched results controller. It can share the managedObjectContext with the first, allowing to access the already faulted objects in that context.
You'll want to structure your predicate / sort descriptor for the controller appropriately, such as:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"artist.name == %#" argumentsArray:[NSArray arrayWithObject:selectedArtist.name]];
You could also scope it by the actual artist object but that should give you the idea.

Related

How to get sectioned data from a CoreData entity relationship in Swift

I have a one to many relationship User -> Weight as shown on the screenshot. I am able to extract sectioned data from an entity using NSFetchRequest and NSFetchedResultsController and then show it on a Table view with sections.
I am able to extract weight data via the User entity. But my question is, how do I extract that weight data such that I could display it on a table view with sections similar to how NSFetchedResultsController does?
Any help would be appreciated. Thanks in advance!
A NSFetchedResultsController can interact with a collectionView or tableView with its objectAtIndexPath and sections property. But you don't need to use it that way. You can create a NSFetchedResultsController on just the User objects with no sectionNameKeyPath. The number of sections is the the fetchedResultsController number of items. And the number of items in each section is equal to each items's number of weights. likewise when you get the delegated callbacks for changes convert the fetchedResultsController's indexPaths to sections.
Ok, so I figured out the best approach to this. Instead of getting the weight data by querying the User entity, I queried the Weight entity instead and used a predicate to return only the weight data of a specific user.
Something like this:
func requestFetch(ofUser selectedUser: User) -> NSFetchRequest{
...
let userPredicate = NSPredicate(format: "user == %#", selectedUser)
...
return fetchRequest
}

NSFetchedResultsController fetchRequest for one-many relationship

I would really appreciate some help with using NSFetchResultsController.
I think what I am need to do should be simple for some people, but I am a bit stuck!
I will try to explain what I am doing, or a simplified version of it.
I have 2 viewControllers, each displaying an NSTableView. I am using Core Data, with an entity Clubs. One of the viewControllers displays a list of Clubs. So for this one, I create an NSFetchResultsController, passing to it a fetchRequest which is basically NSFetchRequest<Clubs>(entityName: “Clubs”).
Each club has a list of Members, with a one-many relationship). I want to display the members using the 2nd viewController. So when I tap a club in the first table, I want the second table to show its members.
I have an NSFetchResultsController connected to the second table. I am stuck at what fetchRequest to send to it. If I use a similar one to the first, i.e. NSFetchRequest<Members>(entityName: “Members”), as expected, I end up with one table showing all of the clubs stored, and the second table showing ALL members stored, whereas I want only the members in the club selected in the first table. I hope that makes sense. So my question is, what fetchRequest should I send to the 2nd tables NSFetchResultsController?
You should add the following predicate to the fetch request underlying the FRC:
fetchRequest.predicate = NSPredicate(format:"club == %#", theClubSelectedInTheFirstViewController)

UITableView and Core Data to-many relationships

I've got a problem with UITableViews and many-to-many relationships.
I have two classes, A and B, that both extend NSManagedObject and live in Core Data. Each has a to-many relationship to the other:
A<<-->>B
I would like to use objects of class A as the sections of a UITableView, and it's set of B objects as "data rows" in the TableView. Is this possible, and what would be the best approach to achieve this? Preferably I would like to use a NSFetchedResultsController to manage the data presented by the UITableView, but so far I have not been able to come up with a predicate and section name key path that works.
You can't use a fetched results controller to do this.
If the A objects are the section names and each B objects has many A relationships then each B can show up in the table multiple times in different sections. Core Data is really set up around the idea of unique objects and all the boilerplate code for tableviews assumes that each row represents a single unique object. In this circumstance, even counting the number of rows would be difficult and the sectionNameKeyPath would be impossible to set because it could have multiple values at any one time.
You need to configure all this by hand. You'll have to fetch the A objects and then set the section titles, then you will have to get each A.bObjs and count and sort them for the rows in each section completely individually. Deleting an exist B object might prove difficult because it could trigger the removal of multiple rows at the same time.
I would urge you to rethink your design. Sections and section titles are not supposed to represent managed objects but rather a grouping of the managed objects represented by the rows based on one of the row object's attributes. What you really want here is a master-detail view hierarchy. The top tableview will show all the A objects. When a row is selected, it loads the second tableview that shows all the B objects related to the selected A object.
I would use NSFetchedResultsController to watch over the A objects. Use your fetched results to populate the section titles. Then when you are looking to populate your "data rows", you can find your current A object (by using [[fetchedResultsController fetchedResults] objectAtIndex:indexPath.section] or something similar) and extract the B objects from it with A.BOjbects, which returns the NSSet of all B objects associated with the A object. After doing that, you can filter or sort the list as you please before placing them in the rows

CoreData model with nested object relationships as a datasource?

I have a very simple data model that consists of 2 objects — a Section and an Item. Each Section has a to-many child relationship to other Section objects as well as a to-one parent relationship with another Section object. Every section has a to-many relationship to Item objects.
Structure aside, some Section objects have no Item objects, and others (at the bottom of the hierarchy) have no Section child objects.
I want to create a tableview that will use Section objects to create the section headers, and then display the Item objects as tableViewCells that are a part of that Section. I also want table headers to appear if the Section has no Items, because seeing the hierarchy is important.
Given a random Section object, how would I go about fetching and displaying this data? Do I need to create a nested loop that flattens the data in an array, or is there some awesome way to leverage predicates and NSFetchedResultsController?
I would build your NSFetchedResultsController with a sort descriptor that sorts the items by the section's ID.
Something like:
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"section.ID" ascending:YES];
This will give your NSFetchedResultsController all the correct items grouped by section. Then you just need to flesh out your table view datasource and delegate accordingly.
Set your fetch entity to Item
Provide the fetch two sort descriptors. The first should sort on section.ID and the second should sort on id. That will return an array of Item objects sorted first by section and then by their own id attribute.
When initializing the fetched result controller, set the sectionNameKeyPath parameter to section.ID. That will cause the section names to display as the Section.ID values.
That should give you a table like this:
Section.ID
Item.ID
Item.ID
Item.ID
Section.ID
Item.ID
.... and so on.

Fetched Results Controller with NSPredicate vs. Core Data NSSet

Okay, so I have a UITableView hierarchy. The top level controller is for Categories, which are selected via Fetched Results Controller with no predicate for the Category Entity.
When a user taps a cell, they are advanced to the Items Table View Controller.
At this point, I assumed I should use another fetched results controller with an NSPredicate to filter out the results not matching the selecting category.
However, I've had a lot of difficulty building a predicate that does this. All the examples seem to be for search strings to attribute comparisons. Not comparing a CoreData Relationship.
So is it a better practice to use this method, or simply get the NSSet of items from the passed in Category Managed Object?
Your second level view controller should have the selected Category set into it via dependency injection as a property. Your second level view controller should only know that "it's" Category is X. It does not need a NSFetchedResultsController at all because you already have all of the information you require via relationships on "it's" Category entity.
To do this, in your top level view controller when a Category is selected you get a pointer to it via the NSFetchedResultsController, instantiate the new view controller, set the Category property and then push the new view controller.
This will give you solid incapsulation and separation between your view controllers.