Swift - Using Core Data With Cocoa - swift

I'm playing with Core Data for an OS X application. The language is Swift. There's something odd about the way it works for Cocoa. The following is a shorter version of what Xcode creates.
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
// MARK: - Core Data stack
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.) This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
let fileManager = NSFileManager.defaultManager()
var failError: NSError? = nil
var shouldFail = false
var failureReason = "There was an error creating or loading the application's saved data."
// Make sure the application files directory is there
do {
let properties = try self.applicationDocumentsDirectory.resourceValuesForKeys([NSURLIsDirectoryKey])
if !properties[NSURLIsDirectoryKey]!.boolValue {
failureReason = "Expected a folder to store application data, found a file \(self.applicationDocumentsDirectory.path)."
shouldFail = true
}
} catch {
let nserror = error as NSError
if nserror.code == NSFileReadNoSuchFileError {
do {
try fileManager.createDirectoryAtPath(self.applicationDocumentsDirectory.path!, withIntermediateDirectories: true, attributes: nil)
} catch {
failError = nserror
}
} else {
failError = nserror
}
}
}()
}
And an SQLite file for Core Data is nowhere to be found after I add a new record to an entity. There's no pointer to an SQLite file in the code above. The following is an iOS counterpart.
class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Core Data stack
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
}
The code above points to an SQLite file. So I don't have trouble inserting a record to an entity.
I'm using Xcode 7.2.1 Am I doing something wrong? Or is this an Xcode bug?

The code you have posted here is only part of a functional Core Data stack, and is insufficient to initialise Core Data.
This is Apple's current example code for initialising a Core Data Stack:
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = psc
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
}
See Initialising the Core Data Stack from Apple.

The Swift 3 version of #DuncanBabbage solution:
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
override init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = psc
DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.appendingPathComponent("DataModel.sqlite")
do {
try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
}

Related

How does an app's core-data get lost when it is updated

My app's users have reported their data got lost when they updated to a new version. The update has nothing to do with Coredata (nothing's changed about it). So, I'm really puzzled why they experience that. I would like your input so I get a clue where I should start looking. I personally have never experienced the data loss on my device, but I am using my app directly installed from Xcode.
// MARK: - Core Data stack
func getManagedObjectContext() -> NSManagedObjectContext {
if managedObjectContext.persistentStoreCoordinator == nil {
let coordinator : NSPersistentStoreCoordinator = getPersistentStoreCoordinator()
managedObjectContext.persistentStoreCoordinator = coordinator
}
return managedObjectContext
}
func getManagedObjectModel() -> NSManagedObjectModel {
guard let modelURL = Bundle.main.url(forResource: "Model", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
return NSManagedObjectModel.init(contentsOf: modelURL)!
}
func getPersistentStoreCoordinator() -> NSPersistentStoreCoordinator {
guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
fatalError("Unable to resolve document directory")
}
let storeURL = docURL.appendingPathComponent("Model.sqlite")
if managedObjectModel == nil {
managedObjectModel = getManagedObjectModel()
}
let persistentStoreCoordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel!)
do {
// MAIN LINE OF CODE TO ADD
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: mOptions)
//The callback block is expected to complete the User Interface and therefore should be presented back on the main queue so that the user interface does not need to be concerned with which queue this call is coming from.
return persistentStoreCoordinator
} catch {
fatalError("Error migrating store: \(error)")
}
}
func saveCoreData (managedObjectContext: NSManagedObjectContext) {
DispatchQueue.main.async {
if managedObjectContext.hasChanges {
var saveDebugTextWhenDone = false
do {
try managedObjectContext.save()
if self.currentParentRecordNameNeedsUpdate {
NotificationCenter.default.post(name: NSNotification.Name("cloudUpdatedCoreDataCurrentParentRecordName"), object: self)
self.currentParentRecordNameNeedsUpdate = false
}
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
}
}
}
}

Migrate Core Data from Swift to SwiftUI

I am attempting to migrate an app from Swift to SwiftUI but am struggling with Core Data. I run both the Swift and SwiftUI apps under the same bundle identifier so they are accessing the same underlying data but although I use the same xcdatamodeld model name for both, they both point to different data bases.
What I need to do is run the app on Swift and load data into Core Data. Then re-run the SwiftUI version of the app and be able to load the identical data.
Here the code for the Swift version:
class DataStore {
static let sharedDataStore = DataStore()
var managedContext: NSManagedObjectContext!
lazy var coreDataStack = CoreDataStack()
fileprivate init() {
self.managedContext = coreDataStack.context
}
func createParcours() -> Parcours {
let parcours = Parcours(context: managedContext)
parcours.timeStamp = NSDate()
return parcours
}
func deleteParcours(_ toDelete: Parcours) {
managedContext.delete(toDelete)
self.saveParcours()
}
func saveContext(parcours: Parcours?) {
if let parcours = parcours {
encodeParcours(parcours)
}
coreDataStack.saveContext()
}
}
class CoreDataStack {
let modelName = "MyParcours" // Exactly same name as name.xcdatamodeld
fileprivate lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
lazy var context: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()
fileprivate lazy var psc: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent(self.modelName)
do {
let options = [NSMigratePersistentStoresAutomaticallyOption: true]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = "There was an error creating or loading the application's saved data." as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
fileprivate lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = Bundle.main.url(forResource: self.modelName, withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
func saveContext () {
guard context.hasChanges else {return}
do {
try context.save()
} catch let error as NSError {
print("Unresolved error: \(error), \(error.userInfo)")
}
}
}
And in the SwiftUI version, I generate the NSPersistentContainer() and inject it into the ContentView:
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "MyParcours")
init() {
container.loadPersistentStores { NSEntityDescription, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
}
}
#main
struct MySwiftUIApp: App {
#StateObject private var dataController = DataController()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}
}
Any pointers where I am going wrong?
I found out why the database did not show up in the SwiftUI version of the app. The reason is that Apple changed the storage location in some earlier version of iOS (not certain exactly when), originally located in the Documents folder and now in the Library/Application%20Support.
So the solution is to change the url of the NSPersistentStoreDescription:
init() {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0].appendingPathComponent("MyParcours")
self.container = NSPersistentContainer(name: "MyParcours")
// Change URL to allow for compatibility with older version in Swift
let description = NSPersistentStoreDescription(url: documentsDirectory)
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { NSEntityDescription, error in
etc.

error with reading SQLite data base with Core Data

i have middle database wich i preload in application, also then i use light weight migration for update this database, but when i have first time called NSPersistentStoreCoordinator i have an error "database disk image is malformed"
for preload database i using following code in my AppDelegate didFinishLaunchingWithOptions
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
if !FileManager.default.fileExists(atPath: url.path) {
let sourceSqliteURLs = [Bundle.main.url(forResource: "SingleViewCoreData", withExtension: "sqlite")!, Bundle.main.url(forResource: "SingleViewCoreData", withExtension: "sqlite-wal")!, Bundle.main.url(forResource: "SingleViewCoreData", withExtension: "sqlite-shm")!]
let destSqliteURLs = [self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite"),self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite-wal"),self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite-shm")]
for index in stride(from:0, to: destSqliteURLs.count, by: 1) {
do {
try FileManager.default.copyItem(at: sourceSqliteURLs[index], to: destSqliteURLs[index])
} catch {
print(error)
}
}
}
then for light weight migration i use new core data model v 2 and add this code to persistentStoreCoordinator
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: mOptions)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
print("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
what I'm doing wrong or how to fix this?
update and install always works when I'm starting app on the device via Xcode and don't works when i use TestFlight versions...

Core Data for iOS 9 in Swift 3 not working

I am developing a project in XCode 8 with Core data Functionality. Xcode 8 does support Core Data functionality for iOS 10 and up but when I try to implement it for iOS 8 and 9, it crashes when getting managedContext with the message:
fatal error: unexpectedly found nil while unwrapping an Optional value
for the code:
let managedContext = appDelegate.managedObjectContext
App Delegate:
// MARK: - Core Data stack
#available(iOS 10.0, *)
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Feel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = Bundle.main.url(forResource: "coreDataTemplate", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if #available(iOS 10.0, *) {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
} else {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
}
Fetching Code:
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
if #available(iOS 10.0, *) {
let managedContext =
appDelegate.persistentContainer.viewContext
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Test")
do {
tests = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
} else {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Test")
do {
let results =
try managedContext.fetch(fetchRequest)
tests = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
How can I implement Core Data for an iOS project with deployment version being iOS 8.2 in XCode 8 using Swift 3?
Are you using a different model name in the iOS 8/9 code? Could you try replacing "coreDataTemplate" with "Feel" in your AppDelegate's managedObjectModel property like this:
let modelURL = Bundle.main.url(forResource: "Feel", withExtension: "momd")!
I'm not sure why it's throwing this particular error, but eliminating the following line might fix it:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
Note that you already have the appDelegate from the guard statement above, and all you're doing here is creating a new constant with the same name, within a lower context. So, you can easily eliminate this line. (Actually, I should think that this downcasting would fail and generate a different error.)
There are a couple other recommendations I would make.
First, most importantly, cut out all the code relating to the Persistent Store
Coordinator. It's redundant. Instead, always use the managedObjectContext property
from the app delegate. That will still work in iOS 10.
(By using both the persistent store coordinator and the pre-iOS 10
code, you are just creating double the work for yourself. And,
you're likely to get a lot more bugs, as you just did here.)
The other recommendation is just a matter of preferred practice. You
can create a managedObjectContext property in each of the view controllers.
And, in the app delegate, set all of them to the app delegate's
managedObjectContext. As a matter of preferred practice, the view
controllers generally should not be referring back to the app
delegate.

CoreData in background thread in Swift 2.0. Clean way?

I have been struggling with this problem for exactly 8 days now. So I believe it's time to ask for help for the Gurus over here.
Ok, so I am trying to implement an app following Uncle Bob's Clean Architecture, so I have the ViewControllers, Models, Interactors, Repositories and Presenters all setup.
My AppDelegate does not have any traces of CoreData in it, nothing. All that is done in the MyAppCoreData class.
The repository is injected in the AppDelegate and the Interactor access the injected object to access not only CoreData stuff, but also Parse and another private API. The Parse and private API are working great.
The CoreData repository also "works". It does not throw any exception. But no data is inserted in CoreData and when I fetch, it's empty.
I believe I am having some problem with the Persistent Store Coordinator + Main Context + Private Context... but have not yet managed to find out what it is.
So let's go to the code:
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let container = Container() { c in
c.register(UserDataStore.self) { _ in ParseRepository() }
c.register(BabyNamesDataStore.self) { _ in BabyNameRepository.sharedInstance }
c.register(GenericRepository.self) { r in
GenericRepository(_userDataStore: r.resolve(UserDataStore.self)!, _babyNameDataStore: r.resolve(BabyNamesDataStore.self)!)
}
}
...
Then, my interactors have the following init() function:
class MainInteractor: MainInteractorInput
{
var output: MainInteractorOutput!
var worker: GenericRepository?
init() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
worker = appDelegate.container.resolve(GenericRepository.self)
}
Then, finally, my CoreDataStore is a singleton (before it wasn't and I could see that it was being instantiated twice for some reason by Swinject):
class BabyNamesCoreDataStore : BabyNamesDataStore{
var mainManagedObjectContext: NSManagedObjectContext
var privateManagedObjectContext: NSManagedObjectContext
static let sharedInstance = BabyNamesCoreDataStore()
init()
{
QL1("BabyNamesCoreDataStore init")
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("BabyNameMe", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
mainManagedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
mainManagedObjectContext.persistentStoreCoordinator = psc
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("BabyNameMe.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch let error as NSError {
QL4("Could not save \(error), \(error.userInfo)")
fatalError("Error migrating store: \(error)")
}
//let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
privateManagedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateManagedObjectContext.parentContext = mainManagedObjectContext
}
deinit
{
do {
try self.mainManagedObjectContext.save()
} catch {
fatalError("Error deinitializing main managed object context")
}
}
func fetchBabyNames(completionHandler: (babyNames: [BabyNames], error: StoreError?) -> Void) {
privateManagedObjectContext.performBlock {
do {
let fetchRequest = NSFetchRequest(entityName: "ManagedBabyNames")
let predicate = NSPredicate(format: "liked == nil")
fetchRequest.predicate = predicate
let results = try self.privateManagedObjectContext.executeFetchRequest(fetchRequest) as! [ManagedBabyNames]
let result = results.map { $0.toBabyName() }
completionHandler(babyNames: result, error: nil)
} catch let error as NSError {
QL4("Could not save \(error), \(error.userInfo)")
completionHandler(babyNames: [], error: StoreError.CannotFetch("Cannot fetch baby names"))
}
}
}
func insertBabyNames(babyNames: BabyNames, completionHandler: (error: StoreError?) -> Void) {
privateManagedObjectContext.performBlock {
do {
let managedBabyNames = NSEntityDescription.insertNewObjectForEntityForName("ManagedBabyNames", inManagedObjectContext: self.privateManagedObjectContext) as! ManagedBabyNames
managedBabyNames.fromBabyName(babyNames)
try self.privateManagedObjectContext.save()
completionHandler(error: nil)
} catch let error as NSError {
QL4("Could not save \(error), \(error.userInfo)")
completionHandler (error: StoreError.CannotCreate("Cannot create baby names with id \(babyNames.id)"))
}
}
}
...
And this is pretty much it. I get no exceptions but it is simply not working.
Could anyone please help this desperate man ? :)
Thanks!
UPDATE
I am following Raymund's Clean Store idea to implement my own, found here: https://github.com/Clean-Swift/CleanStore
I found out that the function that receives the response from the Presenter, inside the View Controller is NOT in the main thread. So I had to add this:
func displaySomething(viewModel: LoginViewModel) {
if viewModel.loginStatus {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.prepareSlideOutVC()
//When user is not logged in, the SlideOut Menu is not loaded. So when he logs in, we must load it.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
dispatch_async(dispatch_get_main_queue()) {
self.router.navigateToMainScene()
}
}
}
}
adding the dispatch_async sorted out the problem in a few cases, but not all of them... still trying to figure out.
So, basically, after I login and receive the response from the Presenter, I activate the segue to go to MainVC but that was in a different thread.
Still trying to work this out.