2
\$\begingroup\$

I have a Core Data model with two entities: Video and Playlist.

  • A Video can be a member of many Playlists
  • A Playlist can have many Videos, including the same Video multiple times
  • A Playlist's Videos are ordered

Video <<----->> Playlist

That being said, I've decided that I need a third entity: Playlist Item.

  • A Playlist Item has an index
  • A Playlist Item can only have a relationship with one Video entity and one Playlist entity

And so, this causes the original specification to be revised...

  • A Video can have many Playlist Items
  • A Playlist can have many Playlist Items, but each Playlist Item has a unique index
  • A Playlist's Playlist Items are ordered using the index attribute

Video <---->> Playlist Item <<----> Playlist

So, the addition of the Playlist Item entity allows me to have many-to-many relationships between Video and Playlist and still maintain ordered lists. Adding this third entity however adds complexity to the overall design.

I am wondering if it is wise to hide that complexity managing the Playlist Item objects in the Video and Playlist implementations.

Video

@interface Video : NSManagedObject

@property (strong, nonatomic) NSSet *playlists; // Custom accessor; Transient attribute; data persisted in Playlist Item
// No declared NSSet for playlistItems

@end

Playlist

@interface Playlist: NSManagedObject

@property (nonatomic, strong) NSArray *videos; // Custom accessor...
// No declared playlistItems here either!

@end

It seems by doing this I will gain code that is better at revealing intentions.

The code smell I'm getting is that NSManagedObjects subclasses are model classes and should therefore not have any controller type glue. Furthermore it makes my Video and Playlist classes completely dependent upon the Playlist Items class. But, all of that said writing the accessor is trivial, which is why I'm considering this approach:

- (NSArray *)videos
{    
    [self willAccessValueForKey:@"videos"];
    NSArray *v = [self primitiveVideos];
    [self didAccessValueForKey:@"videos"];

    if (v) {
        return v;
    }

    NSSet *pi = [self playlistItems];

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES];
    NSArray *orderedPlaylistItems = [pi sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

    NSUInteger count = [orderedPlaylistItems count];
    NSMutableArray *videos = [NSMutableArray arrayWithCapacity:count];
    for (int index = 0; index < count; index++) {
        [videos addObject:[[orderedPlaylistItems objectAtIndex:index] video]];
    }

    v = videos;
    [self setPrimitiveVideos:v];

    return v;
}

(This code is pretty rough, but you get the point, a few lines and voila! an array of video objects from the set of playlist items)

Maybe there is a design pattern or best practice that would help guide my decision?

\$\endgroup\$
3
\$\begingroup\$

Perhaps I'm missing something, but it seems that the Video objects don't really need to know about what playlists they belong to, do they?

A playlist is a collection of videos. You don't need a playlist index object. You just need an array on each playlist that keeps track of what videos are on its playlist and in what order. A simple array does all this for you.

It seems to me that the only reason a video would need to know what playlists it belonged to is if you wanted to provide some functionality for a video to quickly find all playlists it belonged to. And when I say this, I mean a request where it's important for this information to be returned near instantly.

Even without the video knowing any information about the playlists, your code could still iterate through every playlist asking it whether or not it contained a given video and get the answer to what all playlists the video belongs to. This might be a little time consuming, but I think it's a rare request.

But it seems what's most likely is that a user will either want to play an individual video, or play a playlist. The request to play all playlists that contain this particular video seems an odd one to me.

\$\endgroup\$
  • \$\begingroup\$ You are correct, however the Core Data framework forces my hand a bit - to-many relationships are represented as NSSets not NSArrays. That's really the crux of the problem. The backward relationship (from video to playlist) is a consequence of how Core Data models relationships and maintains referential integrity. \$\endgroup\$ – edelaney05 Jul 6 '14 at 15:16
  • \$\begingroup\$ Oh, now I understand. Let me think about this bit and see if I can come up with a better solution. \$\endgroup\$ – nhgrif Jul 6 '14 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.