mbpro
mbpro

Reputation: 2490

Downcasting NSManagedObject in Swift Core Data

I have a class that inherits from NSManagedObject. I'm using this object for model data and it's also being persisted.

class Foo: NSManagedObject {

   @NSManaged var firstVar: String
   @NSManaged var secondVar: String

  let entity = NSEntityDescription.entityForName("Foo",   inManagedObjectContext: managedObjectContext)
  let createdManagedObject = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedObjectContext) as? Foo

}

I can create the object and get the NSManagedObject instance, but I can't downcast it to actual class. Actually, if I downcast it form optional value, then I will get nil value and if I downcast with unwrapping then it will crash. When downcast with nil value debugger inspector shows type: Module.Foo, which I believe is the root of the problem.

Of course I tried with naming the class in .xcmodel inspector, tried to name Entity as Module.Foo, but the latter is not allowed as of Xcode 7 any more anyway.

All together: no success. Now I have to access Foo object through KV, which is kind of awkward.

Anyone solved this issue yet?

EDIT: Adding the code to show creation and down-casting. I had issues with Xcode 7.0 and now same with 7.1.1

Upvotes: 1

Views: 1186

Answers (4)

mbpro
mbpro

Reputation: 2490

It seems that the main problem was the target. Thanks to Martin R for pointing me to the problem. Problem was, that also the code in the main app was part or another target, similar to tests. When object was retrieved from Core Data, it's class signature was Target.Foo and if you tried to cast it to Foo in Target2, you got the error message that cast from Target.Foo to Target2.Foo is not possible and unfortunately this message didn't show in a log unless the log was empty ??

It doesn't matter at all, how you name the Class or Module if at all in setting below:

enter image description here

The only thing that matters is that your .xcmodel is in the same target than the code that accesses it.

Upvotes: 1

mad_manny
mad_manny

Reputation: 1091

You need to instantiate the Foo-Class directly:

let createdManagedObject = Foo(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)

If you still want to encapsule the code to manage CoreData, you could do something similar to the following:

static func newInstanceForEntityName(name: String, context: NSManagedObjectContext) -> NSManagedObject {

    let anyType: AnyObject.Type = NSClassFromString(name)!
    let nsmType: NSManagedObject.Type = anyType as! NSManagedObject.Type

    let entity = NSEntityDescription.entityForName(name, inManagedObjectContext: context)

    return nsmType.init(entity: entity!, insertIntoManagedObjectContext: context)
}

You can use it like:

let foo = newInstanceForEntityName("Foo", context: context) as! Foo

Upvotes: 0

mad_manny
mad_manny

Reputation: 1091

You need to add the objc annotation to your class:

@objc(Foo)
class Foo: NSManagedObject {
   ...
}

Upvotes: 0

Martin Ullrich
Martin Ullrich

Reputation: 100701

You need to edit the data model configuration to tell CoreData which class to use for a specific entity. The actual entity name itself has no impact on the instantiated class.

enter image description here

Upvotes: 0

Related Questions