Jonnny Jonnny - 4 months ago 26
Swift Question

Core Data Modelling from SQL

I am about to start using Core Data for the first time and I'm trying to make sure that my data model is decent before I get too far into it and realizing it's not. I am experienced in SQL and assumed (wrongly is seems) that Core Data was SQLite under a different name. I have read that a big failing of Core Data projects by newcomers is that they try to apply RBMS concepts into Core Data and that isn't necessarily the best way to go. I have mocked up a little SQL schema I was looking to use in this test project. But unsure how it should be altered to fit more appropriately with core data? So if anyone could give some pointers for my scenario that would be great

enter image description here

Answer

Forget persistence (putting it on disk). How would you model this in just in-memory data structures? That's typically very close to how you would model it in Core Data. Core Data is an object graph persistence engine. If your object graph were small enough to fit in memory, in principle you wouldn't need Core Data at all (that's not completely true; Core Data has some other features, but allowing you to efficiently deal with a massive object graph is the headline one).

There are a few differences in Core Data, such as every relationship requiring an inverse (so if Player has a team relationship, then Team should have a players relationship). But basically, model it the way you'd model objects, because that's what they are.

So I don't completely understand some of your model, but a generic "players are on teams and teams have games and games are in a season," I'd imagine a (simplified) model along these lines:

class Player: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var team: Team?  // 0..1 relationship
}

class Team: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var players: Set<Player> // 0..*
    @NSManaged var homeGames: Set<Game> // Inverse for homeTeam (See more below)
    @NSManaged var awayGames: Set<Game> // Inverse for awayTeam

    // There are more powerful and efficient ways to do this, but a simple convenience example
    var games: Set<Game> { return homeGames + awayGames }
}

class Game: NSManagedObject {
    @NSManaged var homeTeam: Team
    @NSManaged var awayTeam: Team
    @NSManaged var season: Season
    @NSManaged var date: NSDate
}

class Season: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var games: Set<Game> // Note again the inverse relationship
}

I made it homeTeam/awayTeam so that it was easy to enforce exactly two teams. But you could also do it this way:

class Team: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var players: Set<Player> // 0..*
    @NSManaged var games: Set<Game> // Many to many relationship
}

class Game: NSManagedObject {
    @NSManaged var teams: Set<Team> // Many to many relationship
    @NSManaged var season: Season
    @NSManaged var date: NSDate
}

Many-to-many relationships aren't a magical thing that you have to create intermediates for in Core Data. They're just "to many" relationships where the inverse is also "to many." There's no need for keys or any of that. It's all just objects. That's how you think about Core Data for the most part.

(I've written this in a form that hopefully makes sense even though it's not absolutely legal in Xcode 7.3. The types of the relationships will be an untyped NSSet. This is all drastically improved in Xcode 8 and iOS 10 and you get strong types, but I haven't done enough work there to be certain if this is quite the syntax or not.)