Reputation: 322
When i try to retrieve an array of items from my model I always get an down cast error, because of a mismatch of the types of the classes. Swift has a strict namespace and the model items are different than the items I want to cat into. Here is my NSManagedObject:
import Foundation
import CoreData
@objc(Boss)
class Boss: NSManagedObject {
@NSManaged var name: String
}
The Testclass is following:
func testCheckIfFetchGetTheCorrectClass() {
// setup item
let entity = NSEntityDescription.entityForName("Boss", inManagedObjectContext: moc)
let boss = Boss(entity: entity!, insertIntoManagedObjectContext: moc)
boss.name = "Chef"
var bosses = [Boss]()
var request = NSFetchRequest(entityName: "Boss")
var e: NSError?
if let results = moc.executeFetchRequest(request, error: &e) {
println("results: \n\(results.description)\nCount:\(results.count)")
if let downcastedSwiftArray = results as? [Boss] {
// downcastedSwiftArray contains only UIView objects
bosses = downcastedSwiftArray
} else {
XCTAssert(false, "Down Cast Error")
}
println("Bosses : \n\(bosses.description)")
} else {
println("fetch error: \(e!.localizedDescription)")
abort();
}
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
When I run the test following types will show up in the debugger:
bosses [NameSpaceTestTests.Boss] 0 values
results [AnyObject] 1 value
[0] Boss_Boss_ *
So it looks like the result
array contains a Boss
class item that will not match the bosses
array.
How can I assign the result of the fetch request to my array?
You will find the complete project on github.
Upvotes: 2
Views: 2015
Reputation: 2932
If you change
if let downcastedSwiftArray = results as? [Boss] {
to force downcast to
if let downcastedSwiftArray = results as! [Boss] {
does your test abort due to a runtime error?
I had problems downcasting to Core Data managed object types as well. Adding the NSManagedObject
sublcasses' files to the test target made the code compile but didn't seem to work properly. So don't add code under test to the test target itself. Import it.
@testable
Before Swift introduced the @testable import
, we were forced to make classes we wanted to test public. That exposed far more of our code to client than we'd want. Nowadays, this is the way to go.
I solved this by making the classes public
:
@objc(Boss)
public class Boss: NSManagedObject {
@NSManaged public var name: String
}
Then import the production module in tests:
import MyProjectTargetName
Upvotes: 4
Reputation: 8631
public
is a workaround and should not be needed for test dependencies.The problem really is that the test target compiles his own version of the source classes, which isn't what you want. You'll end up with two versions of the same.
The following steps fixed it for me:
App
and AppTests
Upvotes: 5
Reputation: 18270
One way to solve this is to dynamically alter the class name of the NSManagedObject
subclass with something like:
let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!
// Check if it is within the test environment
let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
let isTestEnvironment = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
// Create the module name based on product name
let productName:String = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as! String
let moduleName = (isTestEnvironment) ? productName + "Tests" : productName
let newManagedObjectModel:NSManagedObjectModel = managedObjectModel.copy() as! NSManagedObjectModel
for entity in newManagedObjectModel.entities as! [NSEntityDescription] {
entity.managedObjectClassName = "\(moduleName).\(entity.name!)"
}
This helps to workaround the class name mismatch because when running the test, the class name is actually <Product Name>Tests.<Subclass Name>
(in your case, it's NameSpaceTestTests.Boss
)
Upvotes: 0
Reputation: 80273
In Swift you have to specify the Type of the array resulting from a fetch request. The if let pattern requires an optional cast.
if let result = moc.executeFetchRequest(request, error:&e) as? [Boss] {
// use result
}
Upvotes: 0