3

I have a class AEUser which is a subclass of NSManagedObject. That class has a method which creates a new instance of itself. For the default target, the normal route works well:

static func newDefaultUser(moc: NSManagedObjectContext) -> AEUser {
    let newUser = NSEntityDescription.insertNewObjectForEntityForName("AEUser", inManagedObjectContext: moc) as! AEUser
    newUser.uid = sDefaultUser // some string constant
    newUser.name = sDefaultUser // some string constant
    return newUser
  }

However, when executing a XCTestCase which uses the same function to create a new user, a cast error occurs at runtime. I can suddenly not cast the new object into an AEUser anymore. On the default target, everything worked fine though.

Now interestingly the "longer" variant of the above code works flawlessly for the test case. Now a cast is of course not necessary any more. But the above code should produce the exact same object. Also, when using breakpoints and looking into the actual object assigned to newUser above, it seems like it is a AEUser object as should be.

private static func newDefaultUser(moc: NSManagedObjectContext) -> AEUser {
    let entity = NSEntityDescription.entityForName("AEUser", inManagedObjectContext: moc)
    let newUser = AEUser(entity: entity!, insertIntoManagedObjectContext: moc)
    newUser.uid = sDefaultUser // some string constant
    newUser.name = sDefaultUser // some string constant
    return newUser
  }

Can somebody explain why the above code does not work when using XCTestCase / a separate test target, specifically why the runtime cast fails in spite of the above code seemingly producing an object of the proper type just like the second variant?

(Running Xcode 6.3.2 and the iOS 8.3 SDK.)

Raphael
  • 243
  • 1
  • 9

1 Answers1

2

I had a similar problem. Took a bit of debugging to figure out. Hopefully this works for you, too.

I think the issue is that the cast as! AEUser is trying to cast to MyAppTests.AEUser, which presumably doesn't match what your data model wants the class to be (it probably wants MyApp.AEUser). You need to be using MyApp.AEUser in order for the data model to reference the right thing. Try debugging your tests with some breakpoints and create a random instance of AEUser and see what Xcode says the type is. I bet you it's MyAppTests.AEUser.

My solution looked like this: I have an entity called Team that exists in an app called Somersault. So, in my data model (you do the class like this because this):

enter image description here

Then, make sure your NSManagedObject subclass is public, as well as all of its properties:

public class Team: NSManagedObject {
   @NSManaged public var name: String
}

Then in your test file in your test module (mine was SomersaultTests), reference the original module:

import Somersault

And use your subclass as normal. Make sure you do not have your NSManagedObject files under compiled sources for your test module; you want your test module to be referencing the original module.

You should now be able to directly cast your NSManagedObjects like so:

let newTeam: Team = NSEntityDescription.insertNewObjectForEntityForName("Team",
    inManagedObjectContext: self.moc!) as! Team

Good luck.

Community
  • 1
  • 1
Thanizer
  • 382
  • 1
  • 6
  • 20
  • I am using xCode 7 and I can not rename class to Somersault.Team Xcode removes the period so it says SomersaultTeam?? – alex Jan 18 '16 at 06:11