Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unacceptable type of value for to-one relationship: property = "user"; desired type = User; given type = User;

I'm having a wired problem with core data. With Swift3 in iOS 10 I get the managed object context each time I am fetching or storing data with

 func getContext () -> NSManagedObjectContext {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext
}

In my app I have two entities 'User' and 'Ledger'. I want to assign a ledger to a user, but a user can have multiple ledgers. Therefore, I have a UserTableView where I can display the users and a UserViewController class, where I create a user. The same I have for ledger. When creating a ledger I get also a list of all users from which I select one and which should assigned to the ledger and vice versa.

When saving like aforementioned, I get the error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "user"; desired type = User; given type = User;

My data model looks as follows: Data Model

Any help is highly appreciated:)

like image 786
Ben K. Avatar asked Nov 14 '16 21:11

Ben K.


2 Answers

I had this same problem. In my case it was happening while I was running unit tests. In that case, there were two core data stacks in memory at once, one for the application harness and the other for my running unit tests.

The first clue to solving this was to put an assertion right before setting the relationship property to make sure the entity type of the object I was setting was the same as the expected entity type of the relationship. They should be identical, and in my case they were not.

In my case I had a MatchRequest that had a one-to-one relationship with Player called "initiator". So my assertion looked like the following:

    let player = try Player.findLocal(for: matchRequest.initiator, in: moc, createIfMissing: true)
    let expectedEntity = self.entity.relationshipsByName["initiator"]!.destinationEntity!
    assert(player!.entity === expectedEntity, "Player returned doesn't have the same entity type")
    self.initiator = player

The above assertion failed, which I suspect is similar to the assertion used by Core Data that causes the argument exception.

When inspecting Player.entity(), it would return the same entity instance that was causing the failure.

I think the root of the problem is that Core Data is setting some static property for entities that will get incorrectly shared across core data stacks. Calling MyManagedObject.entity() will work correctly when called from one stack but not the other.

So, to work around the problem, when I create my Player object to put into the relationship, I get the entity using the older NSEntityDescription.insertNewObject(...) API, rather than the newer MyManagedObject(context:) constructor. This ensures that the correct entity is used for the given managed object context.

So, to recap:

// SOMETIMES FAILS if you have more than one core data stack:
result = Player(context: managedObjectContext)

// ALWAYS WORKS:
result = NSEntityDescription.insertNewObject(forEntityName: "Player", into: managedObjectContext) as? Player
like image 59
Chris Garrett Avatar answered Nov 15 '22 17:11

Chris Garrett


I had the same issue, but i was very sure I didn't have 2 Core Data Stacks like the previous answer did.

In the end i realized i had initialized a reference to NSStoreCoordinator and a view context and background context using the lazy keyword. I also had a bunch of code doing some heavy lifting in the background thread and saving using said stack.

Apple's docs say: "If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once."

So, this unfortunate scenario is another way to get to this error. Solution: make sure don't lazy anything in your Core Data Stack specially when doing lots of background/foreground thread work.

I hope it helps someone in the future. To you future developer, if you are here: Good luck.

like image 20
Juan Carlos Ospina Gonzalez Avatar answered Nov 15 '22 18:11

Juan Carlos Ospina Gonzalez