Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSFetchedResultsController complex fetch request and model

I'm trying to design my CoreData model for this situation:

We have Song object which have to-many relationship with some List.

Each List have name property and also have to-many relationship with Song

These many-to-many relationship is needed because one Song can be presented in each List at the same time.

I need to somehow obtain list of Songs grouped by these headers (name of List) using NSFetchedResultsController (since user may have 10,000 songs and I don't want to store it somewhere in NSArray).

When I try to set sectionNameKeyPath: with some value, e.g. @"List.name" I've got an error "to-many relationship is not allowed" and of course this makes sense.

So I need help how to design my model to be able to have Songs and some Lists like "History" / "Next Playing" etc. and to be able to fetch all the data with NSFetchedResultsController.

Any help appreciated. If my explanation wasn't clear, please ask additional info.

Part of my code:

- (void)initFetch {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Song"];

    [fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"ANY list.@count != 0"];
    fetchRequest.fetchBatchSize = 20;

    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.context sectionNameKeyPath:@"name" cacheName:nil];
    [self.fetchedResultsController setDelegate:self];

    NSError *error = nil;
    [self.fetchedResultsController performFetch:&error];

    if (error) {
        NSLog(@"Unable to perform fetch.");
        NSLog(@"%@, %@", error, error.localizedDescription);
    }
}
like image 762
Artem Shmatkov Avatar asked May 26 '16 11:05

Artem Shmatkov


2 Answers

Using FRC can be great for displaying data to your user but in using this it can dictate how your model needs to be structured rather than how you might otherwise feel your model entities and relationships should be structured. In your case it looks like you are wanting to see a list of actual States for songs in your database rather than a list a of songs. Each state instance would have a one-one relationship to a song and the state could have a one-to-many relationship in your lists eg. be linked to a History list, playNext list, currentlyPlaying "list" (with only one instance in the last case). By setting your FRC entity to this State entity and section key-path linked to the list that the state is in your could achieve the above requirement without having to manipulate indexPaths or work with multiple FRC's. Also having this separate entity allows you to add other attributes later such as the date this was last played or an aggregate for how times it has been played. All of which could be done on the state entity instead of on the Song entity itself.

like image 188
Dallas Johnson Avatar answered Nov 14 '22 15:11

Dallas Johnson


You don't need to use an FRC to prevent excessive data loading, it isn't the thing which does that. The power of the FRC is its observation of the context to detect changes. That power also doesn't work well when you're trying to navigate across relationships so you're probably also not going to gain from using an FRC in this case.

What limits your data usage is re-faulting managed objects when you don't need them any more (manually in some cases but the system will do this itself when it needs to) and batching the fetch request. When you set a batch size on the fetch request the returned NSArray will deal with only loading pages of data at a time into memory. The FRC benefits from this, but doesn't control it - and if you forget to add the batch the FRC won't do it for you!

So, you can either setup a system where you have multiple arrays, 1 per section, and manage everything yourself. Or you could use multiple FRCs, 1 for each section, and manipulate the index paths so everything hangs together.

Or, you could change your data model by adding intermediate objects which represent the membership of a Song in a List, because this would break the many-to-many relationships and leave you with a navigable relationship in predates and sort descriptors. In this case the new entity would be the one you were fetching for the FRC. Note the above mentioned issue about change tracking across relationships though, take care with what you change and how...

like image 1
Wain Avatar answered Nov 14 '22 16:11

Wain