Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define CoreData relationship in Swift?

In CoreData, I have defined an unordered to-many relationship from Node to Tag. I've created an Swift entity like this:

import CoreData
class Node : NSManagedObject {
    @NSManaged var tags : Array<Tag>
}

Now I want to add a Tag to an instance of Node, like this:

var node = NSEntityDescription.insertNewObjectForEntityForName("Node", inManagedObjectContext: managedObjectContext) as Node
node.tags.append(tag)

However, this fails with the following error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "tags"; desired type = NSSet; given type = _TtCSs22ContiguousArrayStorage000000000B3440D4; value = ( "<_TtC8MotorNav3Tag: 0xb3437b0> (entity: Tag; id: 0xb343800 ; data: {...})" ).'

What is the correct type for to-many relationships?

like image 793
Bouke Avatar asked Jun 22 '14 10:06

Bouke


3 Answers

To be able to work with one-to-many relationship in Swift you need to define property as:

class Node: NSManagedObject {
    @NSManaged var tags: NSSet
}

If you try to use NSMutableSet changes will not be saved in CoreData. And of course it is recommended to define reverse link in Node:

class Tag: NSManagedObject {
    @NSManaged var node: Node
}

But still Swift cannot generate dynamic accessors in runtime, so we need to define them manually. It is very convenient to define them in class extension and put in Entity+CoreData.swift file. Bellow is content of Node+CoreData.swift file:

extension Node {
    func addTagObject(value:Tag) {
        var items = self.mutableSetValueForKey("tags");
        items.addObject(value)
    }

    func removeTagObject(value:Tag) {
        var items = self.mutableSetValueForKey("tags");
        items.removeObject(value)
    }
}

Usage:

// somewhere before created/fetched node and tag entities
node.addTagObject(tag)

Important: To make it all work you should verify that class names of entities in you CoreData model includes your module name. E.g. MyProjectName.Node

like image 152
Keenle Avatar answered Nov 07 '22 02:11

Keenle


As of Xcode 7 and Swift 2.0, the release note 17583057 states:

The NSManaged attribute can be used with methods as well as properties, for access to Core Data’s automatically generated Key-Value-Coding-compliant to-many accessors.

@NSManaged var employees: NSSet

@NSManaged func addEmployeesObject(employee: Employee)
@NSManaged func removeEmployeesObject(employee: Employee)
@NSManaged func addEmployees(employees: NSSet)
@NSManaged func removeEmployees(employees: NSSet)

These can be declared in your NSManagedObject subclass. (17583057)

So you just have to declare the following methods and CoreData will take care of the rest:

@NSManaged func addTagsObject(tag: Tag)
@NSManaged func removeTagsObject(tag: Tag)
@NSManaged func addTags(tags: NSSet)
@NSManaged func removeTags(tags: NSSet)
like image 12
Arnaud Avatar answered Nov 07 '22 03:11

Arnaud


Actually you can just define:

@NSManaged var employees: Set<Employee>

And use the insert and remove methods of the Set directly.

like image 11
FranMowinckel Avatar answered Nov 07 '22 02:11

FranMowinckel