One-to-Many and Many-to-Many Core Data Relationships

Developers are often confused by one-to-many and many-to-many relationships in Core Data, especially when working with Swift. The truth is, with Xcode 8 and Swift 3, it has never been easier to work with Core Data relationships. Let me show you how.

Project Setup

Create a new project and, for convenience, check Use Core Data during the setup of the project.

Setting Up the Project

Setting Up the Project

Open the data model and create two entities, Account and User. The Account entity has no attributes. It only defines a relationship users with the User entity as the destination. Because an account can be linked to many users, the type or cardinality of the relationship is To Many.

Creating the Account Entity

The User entity has two attributes:

  • firstName of type String
  • lastName of type String

It also has a relationship, account, with the Account entity as the destination. The relationship is a To One relationship. Don't forget to set the inverse relationship to users.

Creating the User Entity

Adding a One to Many Core Data

One-to-Many and Many-to-Many Relationships in Core Data

To keep it simple, I will create an account record and a user record in the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Helpers
    let managedObjectContext = persistentContainer.viewContext

    // Create Account
    let account = Account(context: managedObjectContext)

    // Create User
    let user = User(context: managedObjectContext)

    // Configure User
    user.firstName = "Bart"
    user.lastName = "Jacobs"

    return true
}

This is very straightforward in Xcode 8 and with the help of Swift 3. As of Xcode 8.1, Xcode automatically generates a class definition for every entity of the data model. This means that we automatically have access to the User and Account class, subclasses of NSManagedObject.

You can verify this by opening the data model, selecting an entity, and taking a look at the Data Model Inspector on the right. This is what the default settings look like as of Xcode 8.1. The Codegen field is set to Class Definition by default.

Code Generation in Xcode 8 for NSManagedObject Subclasses

It used to be tedious to add a record to a to-many relationship. Two lines of code isn't the end of the world, but it feels clunky and inelegant. And before the introduction of the #keyPath() expression, string literals were the only option.

// Create Mutable Set
let users = account.mutableSetValue(forKey: #keyPath(Account.users))

// Add User
users.add(user)

In Xcode 8, however, this is much more elegant. Xcode automatically creates convenience methods for to-many relationships. Take a look at the updated example below.

account.addToUsers(user)
account.removeFromUsers(user)

This is a welcome improvement. It may seem as if Xcode generates two methods for a to-many relationship, but it actually generates four methods. It generates two flavors of each method. This is what the extension looks like. Xcode generates this extension for us, based on the name of the relationship.

extension Account {

    @objc(addUsersObject:)
    @NSManaged public func addToUsers(_ value: User)

    @objc(removeUsersObject:)
    @NSManaged public func removeFromUsers(_ value: User)

    @objc(addUsers:)
    @NSManaged public func addToUsers(_ values: NSSet)

    @objc(removeUsers:)
    @NSManaged public func removeFromUsers(_ values: NSSet)

}

You can pass in a User instance or an NSSet instance of user records.

If you are working with a one-to-many relationship, it is easier to set the to-one relationship. As you know, Core Data automatically updates the other end of the relationship. The results of the following examples are identical.

account.addToUsers(user)
user.account = account

Xcode 8 and Swift 3 Rock

Core Data has made a significant leap forward with the release of iOS 10, Swift 3, and Xcode 8. Working with Core Data has never been easier and more enjoyable. It is fantastic to see that Apple continues to invest heavily in the Core Data framework. I love it.