How to implement Cora Data Persistent Management - ios

I have the following structure so far:
- Singleton NetworkingManager (login, logout API calls)
- Subclass NSManagedObject (with son extension)
I don't know how to structure this part of the app?
Do I need a PersistentManager/global ObjectManagedContext?
Here are my classes:
NetworkingManager (API-call)
func getContacts() {
GET(APIURL.URL_CONTACTS ,parameters: nil,
{ (operation : NSURLSessionDataTask!, response : AnyObject!) -> Void in
var contacts = [Contacts]()
contacts <<<<* response
//_sharedPersistentManager.save(contacts!)
}, { (operation : NSURLSessionDataTask!, error : NSError!) -> Void in
println("Error contacts")
})
}
Model
import Foundation
import CoreData
class Contacts: NSManagedObject, Deserializable {
#NSManaged var firstname: String
#NSManaged var lastname: String
#NSManaged var id: String
required init(data: [String: AnyObject]) {
let managedContext = (UIApplication.sharedApplication().delegate
as AppDelegate).managedObjectContext!
let entity = NSEntityDescription.entityForName("Contacts", inManagedObjectContext: managedContext)!
super.init(entity: entity, insertIntoManagedObjectContext: managedContext)
self.id <<< data["id"]
self.firstname <<< data["1"]
self.lastname <<< data["2"]
}
}
This code fails... (Bad_ACCESS)
I struggle with the basic class design.
Would be so kind of you to post some pseudo-code and I can understand the basic concept.
All Tutorials explain how to save or fetch some data, but not a real concept for a whole app...
I'm looking for a basic concept like:
- You should implement one Persistent Manager Singleton class
//PersistentManager
- global ObjectManagedContext
- Singleton
- func save (model1: Model1)
- func save (model2: Model2)
- ....
//NetworkingManager
- Singleton
- ...
EDIT:
- added some code
- added further explanation

Typically your App would have one database (but it may have more than one, depending on your requirements). If we assume you only need one then you need to create a Core Data database (persistent store) and at a minimum one NSPersistentStoreCoordinator and one ManagedObjectContext in order to create and update database records via the ManagedObjects. Just use the template app to get sample code for doing this.
I would not try using the ManagedObjects init() method for creating the object, instead use a helper function in another class to do this. This init() method will get called automatically when you create the object so you might use it to set some default values for properties.
Below is an example ManagedObject (Swift class) and an Objective-C helper function. Apologies in advance, you will need to convert this to the equivalent Swift code.
//
// DocumentCategory.swift
// Sample
//
import Foundation
import CoreData
#objc(DocumentCategoryX)
class DocumentCategoryX: NSManagedObject {
#NSManaged var image: NSData
#NSManaged var name: String
#NSManaged var sortIndex: NSNumber
#NSManaged var viewName: String
#NSManaged var documents: NSSet
}
Sample helper function to create a managed object (a Core Data database record)
- (NSManagedObject*)createNewCategory:(NSString*)entityName sortIndex:(int)sortIndex withName:(NSString *)name viewName:(NSString *)viewName icon:(UIImage*)icon;
{
NSManagedObject *newCat = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:[self managedObjectContext]];
[newCat setValue:[NSNumber numberWithInt:sortIndex] forKey:#"sortIndex"];
[newCat setValue:name forKey:#"name"];
[newCat setValue:viewName forKey:#"viewName"];
[newCat setValue:UIImagePNGRepresentation(icon) forKey:#"image"]; // iOS
return newCat;
}
Swift function to save the MOC. Call this whenever you need to make sure things are written to disk.
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save(&error) {
//FLOG("Unresolved error \(error), \(error!.userInfo)")
// Probably need a message to the user warning that a save failed!
}
}
}

Related

Created record in CloudKit with CKAsset but the asset is always nil when fetching, other attributes are ok

I create a record in CloudKit using the CloudKit Dashboard. The record also contains an attribute for a photo, which I upload before saving the record.
The photo data is stored in an attribute of the type CKAsset.
In the entity core data date model it is represented as an attribute of type Data.
When I do a NSFetchRequest later on my local sqlLite DB which synchronises with CloudKit the attribute which is supposed to hold the binary data of the image is always nil.
All the other attributes - which are just strings - are filled with valid data. When I change these attributes and do a NSFetchRequest again the changes are reflected in the fetch result.
I have no idea why the photo attribute is always nil and the other string attributes contain the current valid value.
EDIT - sample code provided.
This is the code which fetches it from the local sqlite DB which is backed by CloudKit and where the photo attribute is nil even it is provided in CloudKit:
let bgContext = self.newBackgroundContext()
bgContext.perform {
do {
fetchRequest.propertiesToFetch = ["title", "categoryValue", "date", "photo", "amount"]
let results = try fetchRequest.execute() as! [ReceiptEntity]
for record in results {
let title = record.title
let photo = record.photo
if let photo_local = photo {
log.info("| Photo attribute is present!!")
}
}
}
} catch let error {
let result: Result<[ReceiptEntity], Error> = .failure(error)
cb(result)
}
This is the Entity definition, generated by Xcode:
extension ReceiptEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<ReceiptEntity> {
return NSFetchRequest<ReceiptEntity>(entityName: "ReceiptEntity")
}
#NSManaged public var additionalInformation: String?
#NSManaged public var amount: Double
#NSManaged public var categoryValue: String?
#NSManaged public var currencyValue: String?
#NSManaged public var date: Date?
#NSManaged public var placeMark: String?
#NSManaged public var title: String?
#NSManaged public var photo: Data?
}
As already mentioned before: When I fetch a certain record from CloudKit directly using the following code - the photo attribute has a CKAsset instance and the photo is there:
privateDB.fetch(withRecordID: configRecordId) { (record, error) -> Void in
let photo = record?.value(forKey: "CD_photo") as? CKAsset
let title = record?.value(forKey: "CD_title") as? String
if let url = photo?.fileURL {
log.info("| Asset URL: \(url)")
}
}
I finally found the problem.
During the iteration of expanding my data model I added attributes manually in CloudKit using the CK dashboard and added the these attributes to my data model.
By adding a CKAsset attribute to CK named CD_photo. But this is wrong. The photo attribute should have had the type "Bytes" and then another attribute named CD_photo_ckAsset of type CKAsset.
But the easiest way to get it right is to let the NSPersistentCloudKitContainer by creating the schema of the App for you.

Convert NSManagedObjects into structs in a "generic" way (Swift)

I have a CoreDataStore class which has two generic placeholders and can be used for each entity type in the model. The idea is that it fetches an NSManagedObject subclass (based on one of the generic types) from the store, converts it into the appropriate object (based on the other generic type) and returns that object.
The purpose of this behaviour is so I'm keeping the Core Data aspects encapsulated and avoiding passing NSManagedObject instances all around the app.
Example potential usage
This is purely how the usage might look to further demonstrate what I am trying to achieve.
let personStore = CoreDataStore<ManagedPerson, Person>()
let personData = personStore.fetchSomeObject() // personData is a value type Person
I have the following code, separated over several files but shown here in a modified fashion for simplicity.
import Foundation
import CoreData
// MARK: - Core Data protocol and managed object
protocol ManagedObjectProtocol { }
class ManagedPerson: NSManagedObject, ManagedObjectProtocol {
var title: String?
}
class ManagedDepartment: NSManagedObject, ManagedObjectProtocol {
var name: String?
}
// MARK: - Simple struct representations
protocol DataProtocol {
typealias ManagedObjectType: ManagedObjectProtocol
init(managedObject: ManagedObjectType)
}
struct Person {
var title: String?
}
struct Department {
var name: String?
}
extension Person: DataProtocol {
typealias ManagedObjectType = ManagedPerson
init(managedObject: ManagedPerson) {
self.title = managedObject.title
}
}
extension Department: DataProtocol {
typealias ManagedObjectType = ManagedDepartment
init(managedObject: ManagedDepartment) {
self.name = managedObject.name
}
}
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol> {
func fetchSomeObject() -> DataObject {
var managedObject: ManagedObject // fetch an NSManagedObject
// Error here
return DataObject(managedObject: managedObject)
}
}
The error I am receiving is when I try to initialise the struct in fetchSomeObject:
Cannot invoke initializer for type 'DataObject' with an argument list of type '(managedObject: ManagedObject)'
Obviously the compiler can't figure out that the DataObject (which is restricted to types conforming to DataProtocol) can be initialised with a ManagedObject (which is restricted to types conforming to ManagedObjectProtocol) despite it being declared as such in DataProtocol.
Is there any way to achieve this functionality? Additionally is this a reasonable approach or am I completely off the wall with this?
Update
After a bit of digging it seems that Swift generics are invariant which I believe is causing what I'm running into.
Think your CoreDataStore again, for example, CoreDataStore<ManagedPerson, Department> doesn't make any sense. Why not? Because the Department is a DataProtocol without problem, but its corresponding typealias ManagedObjectType is not ManagedPerson.
The reason why your code won't compile is just the same. Here return DataObject(managedObject: managedObject) you can't initialize an DataObject from an armbitary ManagedObject, only a DataObject.ManagedObjectType is acceptable.
So what you need is a type constraint, add this where clause, your code should work:
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol
where DataObject.ManagedObjectType == ManagedObject>

Simplified Core Data Stack using Mantle, Overcoat, and Magical Record in union

I've designed a core data stack based on this blog post (in Swift) where I have two NSManagedObjectContext instances, a Main Queue Context (NSMainQueueConcurrencyType) and a Private Queue Context (NSPrivateQueueConcurrencyType) where the main context's job is to deal with all things user interaction related (editing, presenting data to the user) and the private context's only job is to write to disk.
To make managing the stack as easy as possible, I've integrated Magical Record, Overcoat, and Mantle. I've separated all this into two classes, a Core Data singleton stack (built on Magical Record) and a network manager singleton (Built on Overcoat which in turn is built on Mantle).
The Core Data stack looks like this:
import UIKit
import CoreData
import MagicalRecord
class CoreData: NSObject {
enum StackType: Int {
case Default, AutoMigrating, iCloud, inMemory
}
static let sharedStack = CoreData()
private override init() {}
var type: StackType = .Default
func setupStackWithType(type: StackType, withName name: String = MagicalRecord.defaultStoreName()) {
self.type = type
switch self.type {
case .Default:
MagicalRecord.setupCoreDataStackWithStoreNamed(name)
case .AutoMigrating:
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(name)
case .iCloud:
if let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let url = NSURL(fileURLWithPath: documentsPath)
MagicalRecord.setupCoreDataStackWithiCloudContainer(name, localStoreAtURL: url)
} else {
print("Error: could not find documents directory")
}
case .inMemory:
MagicalRecord.setupCoreDataStackWithInMemoryStore()
}
}
func setupStackWithStoreName(storeName: String, automigrating: Bool = true) {
if automigrating {
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(storeName)
} else {
MagicalRecord.setupAutoMigratingCoreDataStack()
}
}
func saveWithBlock(block: (NSManagedObjectContext!) -> ()) {
MagicalRecord.saveWithBlock(block, completion: {
(success, error) in
})
}
func cleanUp() {
MagicalRecord.cleanUp()
}
var managedObjectContext: NSManagedObjectContext {
return NSManagedObjectContext.MR_defaultContext()
}
var privateContext: NSManagedObjectContext {
return NSManagedObjectContext.MR_rootSavingContext()
}
var coordinator: NSPersistentStoreCoordinator {
return NSPersistentStoreCoordinator.MR_defaultStoreCoordinator()
}
var persistentStore: NSPersistentStore {
return NSPersistentStore.MR_defaultPersistentStore()
}
}
My network manager looks like:
import UIKit
import Overcoat
import MTLManagedObjectAdapter
class NetworkManager: OVCManagedHTTPSessionManager {
static let singleton = NetworkManager(baseURL: NSURL(string: Config.ServerBaseEndpoint), managedObjectContext: nil, sessionConfiguration: {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.timeoutIntervalForRequest = 15
config.timeoutIntervalForResource = 15
return config
}())
private override init(baseURL url: NSURL?, managedObjectContext context: NSManagedObjectContext?, sessionConfiguration configuration: NSURLSessionConfiguration?) {
super.init(baseURL: url, managedObjectContext: context, sessionConfiguration: configuration)
self.responseSerializer.acceptableContentTypes = ["text/html", "application/json", "application/xml", "image/png"]
self.securityPolicy = AFSecurityPolicy(pinningMode: .None)
self.securityPolicy.allowInvalidCertificates = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - OVCHTTPSessionManager
override class func modelClassesByResourcePath() -> [String: AnyClass] {
return [Types.RestApi.Post.rawValue:Post.self, "\(Types.RestApi.Post.rawValue)/*": Post.self]
}
}
What I can't quite wrap my head around is 1) how these two classes can work in conjunction and 2) in regards to the core data stack, which context to save on, what work do on on which context, etc.
For NetworkManager.swift (which takes a NSManagedObjectContext to persist models to):
Which context do I initialize the manager with? My assumption would be if you make a network request and that JSON gets transformed into intermediate Mantle models and from there into NSManagedObejct instances, those instances should be saved right to the Private Queue Context, bypassing the Main Queue Context altogether in this instance.
When talking about CoreData.swift:
1) Magical Record has the saveWithBlock method which creates a local context and propagates it up to the root context (in this case the root context is the Private Queue Context) but it's unclear to be what work should be done inside the block.
In their documentation, they give this example:
Person *person = ...;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = #"John";
localPerson.lastName = #"Appleseed";
}];
where they create a Person object outside of the save block and then re-make the entity in the local context then edit it's properties. However in my case, all Person objects would be an instance of MTLModel, not NSManagedObject. When I create some model object, since it isn't a core data object, it wouldn't be inserted into any sort of context until I use MTLManagedObjectAdapter to transform the model into an NSManagedObject instance.
The best way to go about it seems to be create the MTLModel instance(s), do whatever editing is needed, and then either 1) inside saveWithBlock insert the newly created managed objects right into the local context and let it propagate up or 2) insert the objects into the Private Queue Context and save.
2) Do I really need to use the Main Queue Context at all for saving and editing? As I said before, Mantle has the model classes as a subclass of MTLModel and later maps them into NSManagedObject instances so it makes sense that I could just save directly to the Private Queue Context (whose only job is to write to disk anyway)
3) If I don't need to use the Main Queue Context for saving/editing, wouldn't it become the context I use for fetching NSManagedObjects (given that the Private Queue's job is to write to disk and the saving/editing functions of the Main Queue Context seem to be made obsolete by Mantle's intermediate model structure)?

CoreData: Unable to load class for entity

I'm having an issue using relationships in Core Data. I created my data model including the following entities: User, Conversation, Message, Participant - each containing a one-to-many relationship with the entity following it. I generated the classes for each entity using Editor -> Create NSManagedObject Subclass, and it correctly created the .Swift files for each class. The project builds, but when attempting to create and save a new user I get the following error:
2014-12-01 12:31:28.450 Messenger[2627:151403] CoreData: warning: Unable to load class named 'Messenger.User' for entity 'User'. Class not found, using default NSManagedObject instead.
I made sure that my entity classes were prefixed with the project/module name (Messenger.User).
I also added "#ObjC(User)" directly above the User class, and added "-ObjC" to "Other Linker Flags" for the project, as suggested by people in various other posts. These are all the fixes that I could find, but I still get the same error. Here's what my User class looks like, for reference:
import Foundation
import CoreData
#objc(User)
class User: NSManagedObject {
#NSManaged var api : API
#NSManaged var username: String
#NSManaged var userID: String
#NSManaged var passcode: String
#NSManaged var conversations: NSSet
func findConversations(sourceView: MessengerViewController) {
api.findConversations(sourceView)
}
func addConversation(newConversation: Conversation) {
self.addConversationObject(newConversation)
}
}
extension User {
func addConversationObject(value: Conversation) {
var items = self.mutableSetValueForKey("conversations");
items.addObject(value)
}
func removeConversationObject(value: Conversation) {
var items = self.mutableSetValueForKey("conversations");
items.removeObject(value)
}
}
Does anybody have an idea what else I did wrong? I've tried every fix I could come across, but nothing has seemed to work so far.
EDIT: The warning occurs when trying to create a new User object, at the third line below:
let userContext : NSManagedObjectContext = self.appDel.managedObjectContext!
let userEntity : NSEntityDescription = NSEntityDescription.entityForName("User", inManagedObjectContext: userContext)!
var newUser = User(entity: userEntity, insertIntoManagedObjectContext: userContext)
Referring to my own answer, maybe you should also make sure you cast any fetch result to the appropriate class. E.g.
let result = context.executeFetchRequest(request, error:nil) as [User]
In response to your code update, you should perhaps try to insert new instances as follows.
var user = NSEntityDescription.insertNewObjectForEntityForName( "User",
inManagedObjectContext: context) as User

Swift cannot test core data in Xcode tests?

I am working on a iOS project that uses core data. I am using swift.
The Core Data stack is setup right and all seems to be fine.
I have created a class for an entity (NSManagedObject) called TestEntity.
The class looks like this:
import UIKit
import CoreData
class TestEntity: NSManagedObject {
#NSManaged var name: NSString
#NSManaged var age: NSNumber
}
So, then I try to insert a new TestEntity in code using this line of code:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
I then get this error:
I have seen some answers on stack overflow that say that I need to worry about the module name. So then I looked that up on the docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html
Then I went in the core data entity for TestEntity and in the class field I entered myAppName.TestEntity
When I run the app this line:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
still gives me the same error.
What else could I be doing wrong?
EDIT:
So, I was able to make the app not crash anymore by changing the TestEntity NSManagedObject class to:
import UIKit
import CoreData
#objc(TestEntity) class TestEntity: NSManagedObject {
#NSManaged var name: NSString
#NSManaged var age: NSNumber
}
So, I added the #objc(TestEntity) in it. This works with or without adding the appName before the TestEntity class name in the core data data model inspector.
This works, but, when I run tests this line still crashes:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
So I found that this is an issue for other people:
How to access Core Data generated Obj-C classes in test targets?
How can we get core data to work in tests in swift. I am NOT using a bridging header in the app target and it all works great. The test target still crashes though.
How can I fix the test target so it can run core data tests?
With Xcode 7, and #testable, you should no longer need to update the managedObjectClassName or use other hacks. Here's what I did to get it working in Xcode 7.2.
Set your test target Host Application and check "Allow testing Host Applications APIs".
Make sure none of your regular classes have a Target Membership pointing to the Test target. Only classes with unit test code should be set to the Test target.
Add the #testable line to the top of all of your test classes:
import XCTest
#testable import MyApp
class MyAppTests: XCTestCase {
}
If you're still having issues you may want to try these additional tips: https://forums.developer.apple.com/message/28773#28949
I fought with this one for a while so I hope it helps someone else out.
It's because the CoreData framework is still in Objective-C. Swift uses namespaced-classes, so for CoreData to find your swift classes you have to specify the Class name with it's namespace like this:
The problem your will have is that your App does not have the same namespace as when you are running you tests.
<AppName>.<ClassName> vs <AppName>Tests.<ClassName>
EDIT: Solution for running as App and Tests
I just wrote a piece of code to solve the <AppName>.<ClassName> vs <AppName>Tests.<ClassName> issue. The solution I use at this time (Xcode 6.1) is to NOT fill the Class field in the CoreData UI (shown above), and to do it in code instead.
This code will detect if you are running as App vs Tests and use the right module name and update the managedObjectClassName.
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional...
let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!
// Check if we are running as test or not
let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
// Create the module name
let moduleName = (isTest) ? "StreakTests" : "Streak"
// Create a new managed object model with updated entity class names
var newEntities = [] as [NSEntityDescription]
for (_, entity) in enumerate(managedObjectModel.entities) {
let newEntity = entity.copy() as NSEntityDescription
newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
newEntities.append(newEntity)
}
let newManagedObjectModel = NSManagedObjectModel()
newManagedObjectModel.entities = newEntities
return newManagedObjectModel
}()
I think I'm getting similar results to you. I was unable to get my tests working with the line
var newDept = NSEntityDescription.insertNewObjectForEntityForName("Department", inManagedObjectContext: moc) as Department
But I could get the tests running with :
let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: moc)
let department = Department(entity: entity!, insertIntoManagedObjectContext: moc)
My Entity looks like :
#objc(Department)
class Department: NSManagedObject {
#NSManaged var department_description: String
...
}
The code example from Ludovic does not cover subentities. So when setting a parent entity in CoreData, the app crashes.
Adapted the code to take subentities into account:
private func createManagedObjectModel() {
// Get module name
var moduleName: String = "ModuleName"
let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
if isTest { moduleName = "ModuleNameTests" }
// Get model
let modelURL = NSBundle.mainBundle().URLForResource(self.storeName, withExtension: "momd")!
let model = NSManagedObjectModel(contentsOfURL: modelURL)!
// Create entity copies
var newEntities = [NSEntityDescription]()
for (_, entity) in enumerate(model.entities) {
let newEntity = entity.copy() as! NSEntityDescription
newEntity.managedObjectClassName = "\(moduleName).\(entity.managedObjectClassName)"
newEntities.append(newEntity)
}
// Set correct subentities
for (_, entity) in enumerate(newEntities) {
var newSubEntities = [NSEntityDescription]()
for subEntity in entity.subentities! {
for (_, entity) in enumerate(newEntities) {
if subEntity.name == entity.name {
newSubEntities.append(entity)
}
}
}
entity.subentities = newSubEntities
}
// Set model
self.managedObjectModel = NSManagedObjectModel()
self.managedObjectModel.entities = newEntities
}
I also faced similar issue when I tried to write unit test cases for a sample app (MedicationSchedulerSwift3.0) written in Swift 3.0, apart from implementing solution provided by johnford I created a category on XCTestCase to setup an NSManagedObjectContext with in-memory store by using below code:
// XCTestCase+CoreDataHelper.swift
import CoreData
import XCTest
#testable import Medication
extension XCTestCase {
func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])!
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
do {
try persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
} catch {
print("Adding in-memory persistent store failed")
}
let managedObjectContext = NSManagedObjectContext(concurrencyType:.privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
return managedObjectContext
}
}
And used it like this:
// NurseTests.swift
import XCTest
import CoreData
#testable import Medication
class NurseTests: XCTestCase {
var managedObjectContext: NSManagedObjectContext?
//MARK: Overriden methods
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
if managedObjectContext == nil {
managedObjectContext = setUpInMemoryManagedObjectContext()
}
}
//MARK:- Testing functions defined in Nurse.swift
// testing : class func addNurse(withEmail email: String, password: String, inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> NSError?
func testAddNurse() {
let nurseEmail = "clara#gmail.com"
let nursePassword = "clara"
let error = Nurse.addNurse(withEmail: nurseEmail, password: nursePassword, inManagedObjectContext: managedObjectContext!)
XCTAssertNil(error, "There should not be any error while adding a nurse")
}
}
In case if someone needs more examples they can look at unit test cases over here - MedicationTests

Resources