4

I have 2 entities in Core Data, a Bill and Person. I have two relationships:

  • Bill to Person is a 'To Many' relationship called 'people'
  • The inverse - from Person to Bill is 'To One'- called 'bill'.

The result generated by Xcode (along with the classes) are:

Bill:

extension Bill {
    @NSManaged var people: NSSet?
}

Person:

extension Person {
    @NSManaged var bill: Bill?
}

This is the method I've written to add a Person object to the Bill's NSSet:

class Bill : NSManagedObject {
...

func addPersonToBill(contact : CNContact){

        let entDesc = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext())

// Create Person object with custom initialiser
        let person = Person(contact: contact, entity: entDesc!, insertIntoManagedObjectContext: managedObjectContext())
        print("Got person")

        people = people?.setByAddingObject(person) // CRASHES HERE
    }
}

The custom initialiser for Person is:

init(contact : CNContact, entity : NSEntityDescription, insertIntoManagedObjectContext context : NSManagedObjectContext?){
        super.init(entity: entity, insertIntoManagedObjectContext: context)
   // I set other properties here using info from the CNContact
    }

However, when I add a Person, it crashes at people?.setByAddingObject(person) with the error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtCs21_SwiftDeferredNSArray isEqualToSet:]: unrecognized selector sent to instance 0x7fc7ed875cb0'

When I delete the bill relationship from Person to Bill, it runs fine and the Person is added to the Bill's 'people' NSSet. I could leave it like this as I don't necessarily need this inverse relationship, but I've read that it is best to have an inverse relationship unless I have a good reason not to. I also face this issue with other relationships in my app, so I would have a lot of relationships without their inverse.

Any ideas what's wrong? Are my relationships incorrect? One Bill can have many Persons, but one Person can only have one Bill.

The solutions I've tried are:

  • Adding this accessor from this answer:

    @NSManaged func addPeopleObject(value:Person)

But I get the error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSSet intersectsSet:]: set argument is not an NSSet'

  • Creating an NSMutableOrderedSet as suggested by other answers:

let mutPeople = NSMutableOrderedSet(array: (people?.allObjects)!) mutOwers.addObject(person) people = NSSet(array:mutPeople.array)

...but I get the same, original crash message.

  • Unwrapping 'people' - still get the same crash message.

Any help would be much appreciated, thanks.

Community
  • 1
  • 1
Daven
  • 549
  • 4
  • 11
  • Try unwrapping your `people` property. Something like `if let people = self.people as? NSSet { self.people = people.setByAddingObject(person) } else { self.people = NSSet(object:person) }` – Paulw11 Apr 27 '16 at 00:43
  • @Paulw11 I get the same error :/ – Daven Apr 27 '16 at 00:51
  • If you set a breakpoint and look at `people` what class is it? The exception indicates that Swift is converting it to a `SwiftDeferredNSArray` rather than a set – Paulw11 Apr 27 '16 at 00:53
  • @Paulw11 Hmm... when I unwrap `people` and print its `dynamicType` it says it's `_NSFaultingMutableSet`, so it's not an `NSArray` like the error seems to say – Daven Apr 27 '16 at 01:00
  • I think the exception is occurring somewhere inside Core Data as a result of your update rather than directly in that line of code – Paulw11 Apr 27 '16 at 01:01
  • @Paulw11 Ah, I just figured out what I did wrong. Silly mistake on my end, but thank you for pointing me in the right direction. I've posted an answer below. :) – Daven Apr 27 '16 at 01:11
  • 1
    I am interested as to why you had to write code to manage the relationships. Simply assigning `person.bill=bill` should do the trick – Paulw11 Apr 27 '16 at 01:37

3 Answers3

2

Ah, I had a similar problem as [this question].1 I had a method on Bill called getOwers which had a return type of an array, and this seemed to override the accessor CoreData creates. I changed the method to getOwersArr, and all is fine now. Thanks to @Paulw11 for pointing me in the right direction!

Community
  • 1
  • 1
Daven
  • 549
  • 4
  • 11
0

I also have to-many relationships implemented as NSSet. I add/delete objects to the set (without any problems) using code like the following:

let mutableSet = bill.mutableSetValueForKey("people") 

mutableSet.addObject(newPerson)

// (next, save managed object context)
Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
0

I had a similar issue with iOS 9.3 in a one-to-many relationship.

I fixed it by replacing:

group.addToTeams(team) // Crashes on iOS 9.3

with:

team.group = group // This will correctly add team to the set without crashing

Basically instead of adding and object to the set use the Inverse relationship.

Rivera
  • 10,792
  • 3
  • 58
  • 102