When running Unit Tests, CoreData will read the .xcdatamodeld and fill up NSEntityDescription with managedObjectClassName equal to the name of the class that you have specified in your CoreData Model. By default in the corresponding CoreData Configuration this happens to be prefixed by.so if you specify YourClass in the model, in the configuration it will show up as .YourClass instead of just YourClass. The . means "current module". When CoreStore is building the entityName map at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L243 the map will hold YourModule.YourClass or YourModuleTests.YourClass depending what target your are running. However when the map is accessed from your non-test target while running the unit test NSStringFromClass will report YourModule.YourClass as key for the map at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L153 and the force unwrapping will cause a memory access violation.
Solution
Add @objc (YourClass) on top of your NSManagedObject subclass class YourClass: NSManagedObject . This will make it so that NSStringFromClass will return YourClass as opposed to YourModule.YourClass
In the .xcdatamodeld Configuration in the Entity -> Class section enter YourClass and not .YourClass this will remove the module name form the class
Originally created by @tmspzz on GitHub (Feb 9, 2016).
I think adding the following information to the `README.md` would be useful for people trying to use CoreStore in unit tests.
I have the following set up and I think it's quite common.
- I create the CoreStore.defaultStack in the Unit tests
- I use the stack in the non-test Target (i.e. by doing `CoreStore.beginSynchronous`)
## Problem
The problem lies in the discrepancy between the `managedObjectClassName` reported by `NSEntityDescription` (gathered by CoreData from the `.xcdatamodeld`) and the name reported by `NSStringFromClass`. This causes a `EXEC_BAD_ACCESS` at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L153 .
When running Unit Tests, CoreData will read the `.xcdatamodeld` and fill up `NSEntityDescription` with `managedObjectClassName` equal to the name of the class that you have specified in your CoreData Model. **By default in the corresponding CoreData Configuration this happens to be prefixed by** `.`so if you specify `YourClass` in the model, in the configuration it will show up as `.YourClass` instead of just `YourClass`. The `.` means "current module". When CoreStore is building the `entityName` map at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L243 the map will hold `YourModule.YourClass` or `YourModuleTests.YourClass` depending what target your are running. However when the map is accessed from your non-test target while running the unit test `NSStringFromClass` will report `YourModule.YourClass` as key for the map at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L153 and the force unwrapping will cause a memory access violation.
## Solution
- Add `@objc (YourClass)` on top of your `NSManagedObject` subclass `class YourClass: NSManagedObject` . This will make it so that `NSStringFromClass` will return `YourClass` as opposed to `YourModule.YourClass`
- In the `.xcdatamodeld` Configuration in the Entity -> Class section enter `YourClass` and not `.YourClass` this will remove the module name form the class
The problem I see is that your NSManagedObject subclass is declared in a specific module (YourModule) while your .xcdatamodeld is declared in both YourModule and YourModuleTests.
I suggest you either:
explicitly set the entity's Module field in your .xcdatamodeld to your main module's name (Don't leave it at Current Product Module)
explicitly set the entity's Module field in your .xcdatamodeld to blank (Don't leave it at Current Product Module). Then set @objc (YourClass) on your NSManagedObject subclass.
make sure that the Target Membership of your NSManagedObject subclass (.swift file) and your .xcdatamodeld include the same schemes. You probably need to remove them from the Unit Test scheme.
As for including instructions in the README file, I don't think it is within the scope of CoreStore's usage. There's just too many configurations and it should be up to you to make your Swift class match your .xcdatamodeld.
@JohnEstropia commented on GitHub (Feb 9, 2016):
The problem I see is that your NSManagedObject subclass is declared in a specific module (`YourModule`) while your .xcdatamodeld is declared in both `YourModule` and `YourModuleTests`.
I suggest you either:
1. explicitly set the entity's `Module` field in your .xcdatamodeld to your main module's name (Don't leave it at `Current Product Module`)
2. explicitly set the entity's `Module` field in your .xcdatamodeld to blank (Don't leave it at `Current Product Module`). Then set `@objc (YourClass)` on your NSManagedObject subclass.
3. make sure that the `Target Membership` of your NSManagedObject subclass (.swift file) and your .xcdatamodeld include the same schemes. You probably need to remove them from the Unit Test scheme.
As for including instructions in the README file, I don't think it is within the scope of CoreStore's usage. There's just too many configurations and it should be up to you to make your Swift class match your .xcdatamodeld.
I see how this is not strictly relate to CoreStore but maybe a "Common Pitfalls" section would help.
I am also encountering other problems related to unit test. I am getting
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
I have 2 tests in the same Unit Test class which inherits form a base class where on set up I do like so:
class NetworkServiceBaseTestCase: XCTestCase {
override func setUp() {
super.setUp()
CoreStore.defaultStack = DataStack(modelName: "LiG", bundle: NSBundle(forClass: ItemsService.self))
do {
try CoreStore.addInMemoryStoreAndWait()
}
catch {
print("Count not create in memory store")
}
}
}
This code is run before each and every test. Seems like the DataStack should not be initialised more than once? I would like to clear the store on every run.
@tmspzz commented on GitHub (Feb 10, 2016):
I see how this is not strictly relate to CoreStore but maybe a "Common Pitfalls" section would help.
I am also encountering other problems related to unit test. I am getting
```
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
```
I have 2 tests in the same Unit Test class which inherits form a base class where on set up I do like so:
```
class NetworkServiceBaseTestCase: XCTestCase {
override func setUp() {
super.setUp()
CoreStore.defaultStack = DataStack(modelName: "LiG", bundle: NSBundle(forClass: ItemsService.self))
do {
try CoreStore.addInMemoryStoreAndWait()
}
catch {
print("Count not create in memory store")
}
}
}
```
This code is run before each and every test. Seems like the DataStack should not be initialised more than once? I would like to clear the store on every run.
As for your module problems, did any of the suggestions I gave solved it for you?
I suggest you either:
explicitly set the entity's Module field in your .xcdatamodeld to your main module's name (Don't leave it at Current Product Module)
explicitly set the entity's Module field in your .xcdatamodeld to blank (Don't leave it at Current Product Module). Then set @objc (YourClass) on your NSManagedObject subclass.
make sure that the Target Membership of your NSManagedObject subclass (.swift file) and your .xcdatamodeld include the same schemes. You probably need to remove them from the Unit Test scheme.
@JohnEstropia commented on GitHub (Feb 10, 2016):
@blender Please refer to `CoreStoreTests.swift`. There you'll see that the SQLite file is deleted every time:
``` swift
class CoreStoreTests: XCTestCase {
override func setUp() {
super.setUp()
self.deleteStores()
}
override func tearDown() {
self.deleteStores()
super.tearDown()
}
```
In `deleteStores()`'s implementation:
``` swift
private func deleteStores() {
do {
let fileManager = NSFileManager.defaultManager()
try fileManager.removeItemAtURL(
fileManager.URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first!
)
}
catch _ { }
}
```
By doing so, you can start each of your test by creating a new `DataStack` and overwriting any existing ones:
``` swift
func testExample() {
let stack = DataStack(modelName: "Model", bundle: NSBundle(forClass: self.dynamicType))
CoreStore.defaultStack = stack
do {
try stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnModelMismatch: true)
}
```
As for your module problems, did any of the suggestions I gave solved it for you?
> I suggest you either:
> 1. explicitly set the entity's Module field in your .xcdatamodeld to your main module's name (Don't leave it at Current Product Module)
> 2. explicitly set the entity's Module field in your .xcdatamodeld to blank (Don't leave it at Current Product Module). Then set @objc (YourClass) on your NSManagedObject subclass.
> 3. make sure that the Target Membership of your NSManagedObject subclass (.swift file) and your .xcdatamodeld include the same schemes. You probably need to remove them from the Unit Test scheme.
Thanks for the pointers but I am using an In Memory store so I should have to need to delete anything on disk. I am trying now to just created a DataStack and hold on to it instead of assigning it to the CoreStore.defaultStack. I'll se if this solves the issue.
About the previous question I went with option 3 which seems the most clean to me.
You have a truly great README for this project, I have rarely seen such extensive documentation. Although I believe that adding a few information about how to unit test will easy the adoption of your framework.
@tmspzz commented on GitHub (Feb 10, 2016):
Thanks for the pointers but I am using an In Memory store so I should have to need to delete anything on disk. I am trying now to just created a DataStack and hold on to it instead of assigning it to the CoreStore.defaultStack. I'll se if this solves the issue.
About the previous question I went with option 3 which seems the most clean to me.
You have a truly great README for this project, I have rarely seen such extensive documentation. Although I believe that adding a few information about how to unit test will easy the adoption of your framework.
So in Unit Tests creating a DataStack with InMemoryStore and holding on to it works, as opposed to using the defaultStack in the Singleton CoreStore.defaultStack = someStack
Using the Singleton generates the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
@tmspzz commented on GitHub (Feb 10, 2016):
So in Unit Tests creating a `DataStack` with InMemoryStore and holding on to it works, as opposed to using the defaultStack in the Singleton `CoreStore.defaultStack = someStack`
Using the Singleton generates the following error:
```
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
```
I am trying now to just created a DataStack and hold on to it instead of assigning it to the CoreStore.defaultStack.
This should be fine. Just make sure you create a new DataStack for each test and make sure you do fetches from the stack and not from CoreStore.
Using the Singleton generates the following error
I think that's because you're using methods from CoreStore instead of the DataStack. For example, instead of using
CoreStore.beginAsynchronous(...)
you should use
dataStack.beginAsynchronous(...)
The former calls beginAsynchronous() on the CoreStore.defaultStack, which you mentioned you are not assigning to.
@JohnEstropia commented on GitHub (Feb 11, 2016):
Thanks for the feedback!
> I am trying now to just created a DataStack and hold on to it instead of assigning it to the CoreStore.defaultStack.
This should be fine. Just make sure you create a new DataStack for each test and make sure you do fetches from the stack and not from `CoreStore`.
> Using the Singleton generates the following error
I think that's because you're using methods from `CoreStore` instead of the `DataStack`. For example, instead of using
``` swift
CoreStore.beginAsynchronous(...)
```
you should use
```
dataStack.beginAsynchronous(...)
```
The former calls `beginAsynchronous()` on the `CoreStore.defaultStack`, which you mentioned you are not assigning to.
Yes, that was the case, I was calling methods on CoreStore directly. I think that for now I don't have anymore issues. Thanks for the help.
@tmspzz commented on GitHub (Feb 11, 2016):
Yes, that was the case, I was calling methods on CoreStore directly. I think that for now I don't have anymore issues. Thanks for the help.
@blender I created a separate issue to address common mistakes here https://github.com/JohnEstropia/CoreStore/issues/48
Instead of including in the README, I'll try to catch these mistakes from within CoreStore and log suggestions how to fix them.
@JohnEstropia commented on GitHub (Feb 12, 2016):
@blender I created a separate issue to address common mistakes here
https://github.com/JohnEstropia/CoreStore/issues/48
Instead of including in the README, I'll try to catch these mistakes from within CoreStore and log suggestions how to fix them.
This was super helpful. I have been banging my head against a wall for a day on this.
I wish this issue was linked from the main README.md of this project.
@winni2k commented on GitHub (Jan 8, 2017):
This was super helpful. I have been banging my head against a wall for a day on this.
I wish this issue was linked from the main `README.md` of this project.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @tmspzz on GitHub (Feb 9, 2016).
I think adding the following information to the
README.mdwould be useful for people trying to use CoreStore in unit tests.I have the following set up and I think it's quite common.
CoreStore.beginSynchronous)Problem
The problem lies in the discrepancy between the
managedObjectClassNamereported byNSEntityDescription(gathered by CoreData from the.xcdatamodeld) and the name reported byNSStringFromClass. This causes aEXEC_BAD_ACCESSat https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L153 .When running Unit Tests, CoreData will read the
.xcdatamodeldand fill upNSEntityDescriptionwithmanagedObjectClassNameequal to the name of the class that you have specified in your CoreData Model. By default in the corresponding CoreData Configuration this happens to be prefixed by.so if you specifyYourClassin the model, in the configuration it will show up as.YourClassinstead of justYourClass. The.means "current module". When CoreStore is building theentityNamemap at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L243 the map will holdYourModule.YourClassorYourModuleTests.YourClassdepending what target your are running. However when the map is accessed from your non-test target while running the unit testNSStringFromClasswill reportYourModule.YourClassas key for the map at https://github.com/JohnEstropia/CoreStore/blob/master/CoreStore/Internal/NSManagedObjectModel%2BSetup.swift#L153 and the force unwrapping will cause a memory access violation.Solution
@objc (YourClass)on top of yourNSManagedObjectsubclassclass YourClass: NSManagedObject. This will make it so thatNSStringFromClasswill returnYourClassas opposed toYourModule.YourClass.xcdatamodeldConfiguration in the Entity -> Class section enterYourClassand not.YourClassthis will remove the module name form the class@JohnEstropia commented on GitHub (Feb 9, 2016):
The problem I see is that your NSManagedObject subclass is declared in a specific module (
YourModule) while your .xcdatamodeld is declared in bothYourModuleandYourModuleTests.I suggest you either:
Modulefield in your .xcdatamodeld to your main module's name (Don't leave it atCurrent Product Module)Modulefield in your .xcdatamodeld to blank (Don't leave it atCurrent Product Module). Then set@objc (YourClass)on your NSManagedObject subclass.Target Membershipof your NSManagedObject subclass (.swift file) and your .xcdatamodeld include the same schemes. You probably need to remove them from the Unit Test scheme.As for including instructions in the README file, I don't think it is within the scope of CoreStore's usage. There's just too many configurations and it should be up to you to make your Swift class match your .xcdatamodeld.
@tmspzz commented on GitHub (Feb 10, 2016):
I see how this is not strictly relate to CoreStore but maybe a "Common Pitfalls" section would help.
I am also encountering other problems related to unit test. I am getting
I have 2 tests in the same Unit Test class which inherits form a base class where on set up I do like so:
This code is run before each and every test. Seems like the DataStack should not be initialised more than once? I would like to clear the store on every run.
@JohnEstropia commented on GitHub (Feb 10, 2016):
@blender Please refer to
CoreStoreTests.swift. There you'll see that the SQLite file is deleted every time:In
deleteStores()'s implementation:By doing so, you can start each of your test by creating a new
DataStackand overwriting any existing ones:As for your module problems, did any of the suggestions I gave solved it for you?
@tmspzz commented on GitHub (Feb 10, 2016):
Thanks for the pointers but I am using an In Memory store so I should have to need to delete anything on disk. I am trying now to just created a DataStack and hold on to it instead of assigning it to the CoreStore.defaultStack. I'll se if this solves the issue.
About the previous question I went with option 3 which seems the most clean to me.
You have a truly great README for this project, I have rarely seen such extensive documentation. Although I believe that adding a few information about how to unit test will easy the adoption of your framework.
@tmspzz commented on GitHub (Feb 10, 2016):
So in Unit Tests creating a
DataStackwith InMemoryStore and holding on to it works, as opposed to using the defaultStack in the SingletonCoreStore.defaultStack = someStackUsing the Singleton generates the following error:
@JohnEstropia commented on GitHub (Feb 11, 2016):
Thanks for the feedback!
This should be fine. Just make sure you create a new DataStack for each test and make sure you do fetches from the stack and not from
CoreStore.I think that's because you're using methods from
CoreStoreinstead of theDataStack. For example, instead of usingyou should use
The former calls
beginAsynchronous()on theCoreStore.defaultStack, which you mentioned you are not assigning to.@tmspzz commented on GitHub (Feb 11, 2016):
Yes, that was the case, I was calling methods on CoreStore directly. I think that for now I don't have anymore issues. Thanks for the help.
@JohnEstropia commented on GitHub (Feb 12, 2016):
@blender I created a separate issue to address common mistakes here
https://github.com/JohnEstropia/CoreStore/issues/48
Instead of including in the README, I'll try to catch these mistakes from within CoreStore and log suggestions how to fix them.
@winni2k commented on GitHub (Jan 8, 2017):
This was super helpful. I have been banging my head against a wall for a day on this.
I wish this issue was linked from the main
README.mdof this project.