1

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

mbpro
  • 2,280
  • 2
  • 20
  • 35
  • 3
    Can you please show how you fetch it and how you downcast it? – mad_manny Nov 10 '15 at 08:59
  • 2
    Did you try the various solutions in http://stackoverflow.com/questions/25076276/unable-to-find-specific-subclass-of-nsmanagedobject and http://stackoverflow.com/questions/26613971/coredata-warning-unable-to-load-class-named? Does this happen in your main program or a unit test or in a framework? – Martin R Nov 10 '15 at 09:17
  • I'm actually working on a project with core data and I have no issues doing: `self.entry = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext: managedObjectContext) as? Foo` . Can you post more details? – Andrei Nagy Nov 10 '15 at 10:20
  • @mad_manny, done. It's pretty much straightforward. – mbpro Nov 10 '15 at 10:55
  • @MartinR, all of this possibilities were tested. With or without module and it happens in main program and test. – mbpro Nov 10 '15 at 10:58
  • @AndreiNagy, can you access properties like Foo.someProperty or you access them with KV pattern? – mbpro Nov 10 '15 at 10:59
  • @mbpro: You might have to clean the project after you changed the class or module in the Core Data model inspector. – Martin R Nov 10 '15 at 11:00
  • @MartinR Of Course. Cleaned it, deleted derived data, restarted Xcode 7.1, deleted app... still, no change – mbpro Nov 10 '15 at 11:05
  • Did you try `Foo(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)` – mad_manny Nov 10 '15 at 11:33
  • @mad_manny, yes, as seen from code sample – mbpro Nov 10 '15 at 11:35
  • In your sample I see `NSManagedObject(...) as? Foo` not `Foo(...)`. Or did you try both? – mad_manny Nov 10 '15 at 11:37
  • @mad_manny If you force unwrap it, then it will crash, to optional casting gives you back nil as unsuccessful cast. – mbpro Nov 10 '15 at 11:44
  • if you instantiate Foo directly as in my earlier comment, you don't need to cast and also this doesn't result in an optional. Still my question: Did you try this? `let createdManagedObject = Foo(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)` – mad_manny Nov 10 '15 at 11:54
  • @mad_manny, that will work, of course, but it won't give me flexibility to encapsulate handling Core Data in reusable code. – mbpro Nov 10 '15 at 12:37

4 Answers4

1

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.

mbpro
  • 2,280
  • 2
  • 20
  • 35
0

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

Martin Ullrich
  • 78,211
  • 20
  • 211
  • 189
  • Yes, this was done, of course. Problem is that returned class is not Foo, but Module.Foo, therefore the downcast is unsuccessful. – mbpro Nov 10 '15 at 10:59
  • You should use ".Foo" (dot at the beginning) and not explicitly specify the module.. it will be determined at build time. Then `NSEntityDescription.insertNewObjectForEntityForName(entityName: String, inManagedObjectContext context: NSManagedObjectContext)` should work – Martin Ullrich Nov 10 '15 at 14:08
0

You need to add the objc annotation to your class:

@objc(Foo)
class Foo: NSManagedObject {
   ...
}
mad_manny
  • 1,011
  • 15
  • 26
  • Tried. Doesn't help. Still, I believe if you inherit from NSObject or subclass, @objc directive is not needed, it should be inferred. – mbpro Nov 10 '15 at 11:21
  • In my case this was needed and I had extended NSManagedObject. – mad_manny Nov 10 '15 at 11:23
  • If you set the module of the entity to "Current Product Module" then you must *not* add `@objc(...)`, at least that it what I experienced. – Martin R Nov 10 '15 at 11:56
0

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
mad_manny
  • 1,011
  • 15
  • 26
  • This is actually the way, how I've done it in another class, but thanks for showing this! :) – mbpro Nov 10 '15 at 15:49