24

I have a NSManagedObject class

class Disease: NSManagedObject {
    @NSManaged var diseaseId: String
    @NSManaged var diseaseName: String
    @NSManaged var dogBreed: NSSet
}

How do I add a new relationship to dogBreed? If i change var type to NSMutableSet, the changes are not saved to database. Any ideas?

tadasz
  • 4,366
  • 5
  • 26
  • 33
  • more info http://stackoverflow.com/questions/24146524/setting-an-nsmanagedobject-relationship-in-swift – DogCoffee Nov 23 '15 at 02:39
  • For others that find this question because they want to learn how to use relations in Core Data, i finally found this tutorial: [Core Data and Swift: Relationships and More Fetching](http://code.tutsplus.com/tutorials/core-data-and-swift-relationships-and-more-fetching--cms-25070) – Jakob Hviid PhD Nov 23 '15 at 13:15

7 Answers7

21

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 addDogBreedsObject(dogBreed: DogBreed)
@NSManaged func removeDogBreedsObject(dogBreed: DogBreed)
@NSManaged func addDogBreeds(dogBreeds: NSSet)
@NSManaged func removeDogBreeds(dogBreeds: NSSet)
Arnaud
  • 17,268
  • 9
  • 65
  • 83
  • 1
    Thanks for this! + 100 – DogCoffee Nov 23 '15 at 00:27
  • 1
    This might be strange and late but how does Swift know how to take care of the rest? You do not specify to which set the functions must add? – Emptyless Apr 28 '16 at 19:22
  • 2
    @Emptyless This has little to do with Swift. Those methods are generated by CoreData, it's the NSManaged class that actually takes care of it. The above answers just explains the proper way to do so with Swift. It's quite similar to declaring IBOutlets in UIViewControllers, and you asking how Swift knows how to link that to the Storyboard (it's not Swift either, it's UIKit) – Arnaud Apr 28 '16 at 19:35
  • 2
    You say they are generated but maybe I am missing something? I have added two relationships from a club to members and vice versa. I have set them to be to many relationships and have the inverse pointed at themselves. Still when I generate the NSManagedSubclasses there are no methods in those. Only the NSSet, thus leading to my question how to point those methods to the NSSet. Thanks in advance! – Emptyless Apr 28 '16 at 19:43
  • 2
    @Emptyless There are the underlying Key-Value methods that CoreData generates on one hand, and the Swift methods that rely on those methods on the other hand. When you generate the NSManagedSubclasses, XCode generates some (but not all) of said Swift methods to save you some time. You have to manually write the ones it does not generate for you even though the underlying KV method exists already. – Arnaud Apr 29 '16 at 12:51
18

Swift cannot generate dynamic runtime accessors due to strict type system. You can create an extension to Disease class and add missed methods manually, here is the code:

extension Disease {
    func addDogBreedObject(value:DogBreed) {
        var items = self.mutableSetValueForKey("dogBreed");
        items.addObject(value)
    }

    func removeDogBreedObject(value:DogBreed) {
        var items = self.mutableSetValueForKey("dogBreed");
        items.removeObject(value)
    }
}

Remarks:

I suggest you to create separate file for extension Disease+CoreData.swift, This should help to keep your code from overrides when you re-generate the CoreData model.

It is sufficient to create relationship in one of managed objects, second one will be updated with back reference. (Same as was with Objective-C)

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.Disease

Answer inspired by: Setting an NSManagedObject relationship in Swift

Community
  • 1
  • 1
Keenle
  • 12,010
  • 3
  • 37
  • 46
  • 1
    I found that this code would not work for my app without wrapping the setting process in the proper key value observation code, to whit self.willChangeValueForKey("key", withSetMutation:, usingObjects:) and the corresponding didChangeValueForKey method. Without this all kinds of strange and apparently unrelated errors were reported. – Ash Jan 27 '15 at 11:02
  • Good point. I'll test your suggestion and add to the answer when back to my dev desktop. – Keenle Jan 27 '15 at 11:10
12

If you are looking for a simple approach just use this.

import CoreData
extension NSManagedObject {
    func addObject(value: NSManagedObject, forKey: String) {
        var items = self.mutableSetValueForKey(forKey);
        items.addObject(value)
    }
}

The implementation:

department.addObject(section, forKey: "employees")

Instead of writing the same method on every NSManageObject class that needs it.

naz
  • 1,478
  • 2
  • 21
  • 32
5

Actually you can just write:

@NSManaged var dogBreed: Set<DogBreed>

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

FranMowinckel
  • 4,233
  • 1
  • 30
  • 26
3

As an extension to @Naz. And in combination with @Ash's comment. I've found the only working solution for xCode 6.3.1

import CoreData

extension NSManagedObject {
    func addObject(value: NSManagedObject, forKey: String) {
        self.willChangeValueForKey(forKey, withSetMutation: NSKeyValueSetMutationKind.UnionSetMutation, usingObjects: NSSet(object: value) as Set<NSObject>)
        var items = self.mutableSetValueForKey(forKey);
        items.addObject(value)
        self.didChangeValueForKey(forKey, withSetMutation: NSKeyValueSetMutationKind.UnionSetMutation, usingObjects: NSSet(object: value) as Set<NSObject>)
    }
}

You have to call the willChange and didChange handlers in order to correctly add an object to a to-many relationship in Swift.

myManagedObject.addObject(value, forKey: "yourKey")
Simon
  • 31
  • 2
2

@Nycen has the best solution for Xcode 7 with the now automatically implemented methods, however note that the NSOrderedSet issue almost unbelievably still exists (see this question), so if using an NSOrderedSet for your relationship instead of an NSSet you will still need to use a workaround.

(I would have commented instead of posting a new answer, however I am lacking in points)

Community
  • 1
  • 1
DJB
  • 257
  • 2
  • 11
1

If you need to use an NSOrderedSet instead of NSSet, I've been using the following code which makes it easier to get at the NSMutableOrderedSet methods for adding, inserting, and removing objects.

@NSManaged private var myOrderedSet: NSOrderedSet

var myMutableOrderedSet: NSMutableOrderedSet {
    return self.mutableOrderedSetValueForKey("myOrderedSet")
}
blwinters
  • 1,911
  • 19
  • 40