Как определить отношение CoreData в Swift?

В CoreData я определил неупорядоченное отношение ко многим из Node в Tag, Я создал объект Swift, как это:

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

Теперь я хочу добавить Tag к примеру Node, как это:

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

Однако это происходит со следующей ошибкой:

Завершение работы приложения из-за неперехваченного исключения "NSInvalidArgumentException", причина: "Недопустимый тип значения для отношения ко-многим: property = "tags"; желаемый тип = NSSet; данный тип = _TtCSs22ContiguousArrayStorage000000000B3440D4; значение = ("<_TtC8MotorNav3Tag: 0xb3437b0> (сущность: тег; id: 0xb343800; данные: {...})").'

Каков правильный тип для отношений ко многим?

4 ответа

Решение

Чтобы иметь возможность работать с отношением один-ко-многим в Swift, вам нужно определить свойство как:

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

Если вы попытаетесь использовать NSMutableSet изменения не будут сохранены в CoreData. И, конечно, рекомендуется определить обратную ссылку в Node:

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

Но все же Swift не может генерировать динамические средства доступа во время выполнения, поэтому нам нужно определить их вручную. Их очень удобно определять в классе extension и положить в Entity+CoreData.swift файл. Сильфонное содержание Node+CoreData.swift файл:

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)
    }
}

Использование:

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

Важное замечание : Чтобы все это работало, вы должны убедиться, что имена классов сущностей в вашей модели CoreData включают имя вашего модуля. Например MyProjectName.Node

Начиная с Xcode 7 и Swift 2.0, в примечании к выпуску 17583057 говорится:

Атрибут NSManaged может использоваться как с методами, так и со свойствами, для доступа к автоматически генерируемым методам Key Data-Key-Value-Coding-совместимым для многих методов доступа.

@NSManaged var employees: NSSet

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

Они могут быть объявлены в вашем подклассе NSManagedObject. (17583057)

Так что вам просто нужно объявить следующие методы, а Core Data позаботится обо всем остальном:

@NSManaged func addTagsObject(tag: Tag)
@NSManaged func removeTagsObject(tag: Tag)
@NSManaged func addTags(tags: NSSet)
@NSManaged func removeTags(tags: NSSet)

На самом деле вы можете просто определить:

@NSManaged var employees: Set<Employee>

И использовать insert а также remove методы Set непосредственно.

Основываясь на ответе @Keenle, если вы хотите быть нахальным, лаконичным и уметь говорить

node.tags.append(tag)

можно обернуть вызов self.mutableSetValueForKey:

class Node: NSManagedObject {

    var tags: NSMutableOrderedSet {
        return self.mutableOrderedSetValueForKey("tags")
    }
}
Другие вопросы по тегам