how to fix attempt to recursively call -save error with coredata? - ios

I am getting this error randomly while saving in core data
Unresolved error Error Domain=NSCocoaErrorDomain Code=132001 "(null)" UserInfo={message=attempt to recursively call -save: on the context aborted, stack trace=(
Everything is working fine for last 3 month but recently I due to change in app I have to call a lot of fetch and save request and some of them are in loop and some in closure after making these changes I faced this error.
Here is code for coredata manager
import Foundation
import CoreData
class CoreDataStack {
private init() {
}
class func getContext () -> NSManagedObjectContext {
return CoreDataStack.managedObjectContext
}
// MARK: - Core Data stack
static var managedObjectContext: NSManagedObjectContext = {
var applicationDocumentsDirectory: URL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.cadiridris.coreDataTemplate" in the application's documents Application Support directory.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
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: "Thyssenkrupp", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
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: managedObjectModel)
let url = applicationDocumentsDirectory.appendingPathComponent("Thyssenkrupp.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
let options = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption:true ]
do {
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] = 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
}()
// 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 = persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
class func saveContext () {
DispatchQueue.main.async {
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
print("Unresolved error \(nserror), \(nserror.userInfo)")
//abort()
}
}
}
}
}
Please provide any suggestion why this error coming

The problem was saving data to CoreData to frequently, Yes you can CoreData as Frequently as you want but it will through this error on console if you add/delete/update a data and save it in a loop doing this way will cause this error, not always but it's better to save CoreData after loop is complete. As Saving to Core Data is important in case where we perform Create,Update, Delete operation and we didn't save the CoreData app crashes/closed down for some reason then data will be lost from the point we last save the CoreData. Saving to CoreData is like a checkpoint everything is saved. So saving in a loop is not efficient way to do.

Related

Is there any other way than `UserDefaults` to share data between `Today Widget Extension` and the main `Container Application` in iOS

I mean I want to know that is it possible to use CoreData which shares a common resource?
You have the options of sharing the SQLite and user default between your main app and the widgets.
If you want the SQLite to be shared between the Main App and the widget, please follow the steps.
create an app group. Click on the main project -> Capabilities -> App Group -> create a group.. example: group.com.appname.appgroup
While creating the persistent coordinator, create an SQLite file in the app group and provide the path.
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and return 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 directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.appname.appgroup")!
let url = directory.appendingPathComponent("somename.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: [NSMigratePersistentStoresAutomaticallyOption:true,NSInferMappingModelAutomaticallyOption:true])
} 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}()
You can access the core data in widgets like the way you use in main app.
If you want to use the user defaults, then follow the below steps.
create a user deafult object.
var userDefaults =
UserDefaults(suiteName:"group.com.appname.appgroup")!
use this object for setting and retrieving the values.
You can share data between extensions and the host app via Keychain Sharing and App Groups (needs to be configured in the capabilities of your target).
Example for Keychain Sharing with KeychainAccess:
import KeychainAccess
let sharedKeychain = Keychain(service: "com.company.App.shared" , accessGroup: "TeamId.App")
sharedKeychain?["username"] = "Test"
Example for User Defaults:
var userDefaults = UserDefaults(suiteName: "group.com.company.App")!
Example for data in App Group:
let fileUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.company.App")!
To share data via CoreData just put the database file into the App Group storage.
Example with CoreStore:
import CoreStore
let dataStack: DataStack = {
let dataStack = DataStack(xcodeModelName: "App")
let storagePathUrl = fileUrl.appendingPathComponent("App.sqlite")
do {
try dataStack.addStorageAndWait(SQLiteStore(fileURL: storagePathUrl, configuration: "Default", localStorageOptions: .
recreateStoreOnModelMismatch))
} catch let error {
print("Cannot set up database storage: \(error)")
}
return dataStack
}()

How to use persistentStore(for: url) in core data Swift 3

I'm trying to do the following:
Tap a cell of a UITableView cell then segue to the next UIViewController and display the database results. But there are multiple persistent stores therefore the designated store is specified by the cell label text.
The question is: How to use the method persistentStore(for: url)? Or is there some else way to specify a persistent store for the fetchRequest?
Here is my code that is not working:
func wordFetchRequest() -> NSFetchRequest<Word> {
let fr = NSFetchRequest<Word>(entityName: "Word")
fr.fetchBatchSize = 100
// Assigning sort descriptors
let firstLetterSort = NSSortDescriptor(key: #keyPath(Word.firstLetter), ascending: true)
let spellSort = NSSortDescriptor(key: #keyPath(Word.spell), ascending: true)
fr.sortDescriptors = [firstLetterSort, spellSort]
// Get URL of the designated store to fetch
let libname = (AppDelegate.nameDict as NSDictionary).allKeys(for: nameToFetch).first!
// I'm not sure the following line: which file should I use? I've tried
//.sqlite, .sqlite-shm and .sqlite-wal but none worked.
let url = AppDelegate.coreDataStack.storeDirectory.appendingPathComponent("\(libname).sqlite-wal")
// Specify affected store for the fetch request
var pss = [NSPersistentStore]()
print(url)
// The following line fails:
if let ps = coreDataStack.psc.persistentStore(for: url) {
pss.append(ps)
} else {
}
fr.affectedStores = pss
print(fr.affectedStores ?? "No stores available.")
return fr
}
Any help will be much appreciated.
I had to deal with similar scenario where I had different persistent stores (to be specific one of type NSInMemoryStoreType and other of type NSSQLiteStoreType to be specific)
I found it easier to create separate persistent store coordinators for each store and create separate managed object context using these persistent stores as there parent stores :)
Here is the code which was written in iOS 9 swift 3, hence has older core data stack operations, I have seen iOS 10 Swift 3 Core data stack, I believe these methods can still give you idea of what am talking here :)
This is what you will see by default in Coredata stack, getter for persistentStoreCoordinator
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)
log.debug(url)
} catch let error as NSError {
// 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
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()
}
catch{
}
return coordinator
}()
Important statement here though is
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
As you can see it specifies the persistent store type as Sqlite and specifies the configurationName as nil, which means default configuration :)
You can create multiple configurations in Coredata and specify there name in this statement to create separate persistent store coordinator for each configurations :)
You can have a look at my blog Can core data be trusted with sensitive informations to see how you can create multiple configurations and stores :)
So lets assume you create another configuration and added entities to them and lets call it as "Test1" configuration, you will create a separate persistent store coordinator for that using,
lazy var test1PersistentStoreCoordinator: 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: NSInMemoryStoreType, configurationName: "Test1", at: url, options: nil)
log.debug(url)
} catch let error as NSError {
// 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
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()
}
catch{
}
return coordinator
}()
Now you have two persistent store coordinators associated with two different configurations, simply create two managed object contexts using these persistent store coordinators as their parent store :)
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
}()
lazy var managedObjectContextForBackTracking : NSManagedObjectContext = {
let coordinator = self.test1PersistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
Thats it :)
Now run your fetch requests on corresponding managedObject contexts :) and be sure that nothing messes up your core data :)
Hope it helps :)

Batch delete request crashes app

I have an InMemory Store Coordinator declared like so:
lazy var ramStoreCoordinator: 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)
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, 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
}()
as well as an associated ManagedObjectContext:
lazy var ramManagedObjectContext: 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.ramStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return managedObjectContext
}()
I'm trying to execute a fetch request like so:
let fetchRequest = NSFetchRequest(entityName: "Post")
let batchDelete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
// Execute Batch Request
try ramManagedObjectContext.executeRequest(batchDelete)
} catch {
let updateError = error as NSError
print("\(updateError), \(updateError.userInfo)")
}
the line:
try ramManagedObjectContext.executeRequest(batchDelete)
crashes the app with the following output:
2016-04-30 23:47:40.271 Secret[2368:1047869] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unknown command type (entity: EntityName; predicate: ((null)); sortDescriptors: ((null)); type: NSManagedObjectIDResultType; ) >'
* First throw call stack:
(0x18145ae38 0x180abff80 0x1833710b0 0x18338991c 0x183391d64 0x101121a3c 0x10112d5f0 0x1833845bc 0x1832c1d5c 0x183354e04 0x10011abc4 0x10011947c 0x100092bf0 0x10009269c 0x1000926ec 0x186a1aac0 0x186a1b258 0x186901854 0x186904a4c 0x1866d4fd8 0x1865e0014 0x1865dfb10 0x1865df998 0x183f4da20 0x101121a3c 0x1011274e4 0x181410dd8 0x18140ec40 0x181338d10 0x182c20088 0x18660df70 0x1000fcba8 0x180ed68b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
In my experience, NSBatchDeleteRequest won't work with NSInMemoryStoreType.
I was trying the same to test my methods, but I will have to hold back on that.
Edit:
Adding Marcus Zarra's tweet as proof.
I had the exact same problem. Solved it by changing the in memory store to NSSQLiteStoreType . Have not done any research why this happens to in memory store, but I hope that solves your problem.
NSBatchDeleteRequest should be executed on your ramStoreCoordinator, not ramManagedObjectContext, since it works directly with NSPersistenceStore class instance:
try persistentStoreCoordinator.executeFetchRequest(batchDelete, withContext: ramManagedObjectContext)
Check this link for more details:
https://developer.apple.com/videos/play/wwdc2015/220/
Hope it helped)
This error can occur when you rename some files outside XCode. To solve it you can just remove the files from your project (Right Click - Delete and "Remove Reference") You re-import the files in your project and everything will be ok !
If it didn't fix, try this
try persistentStoreCoordinator.executeFetchRequest(
batchDelete, withContext:context
)
as NSBatchDeleteRequest is executed on the persistent store coordinator, not the managed object context.

How should I correctly manage a full Core Data stack in private queue?

I handle two complete Core Data stacks in my app:
The one that is provided by default in AppDelegate.
A second stack I fully create in order to perform NSManagedObject updates in a private queue, to avoid blocking the UI.
I have a class to create the second "auxiliary" Core Data stack, and I do this way:
class CoreDataStack: NSObject {
class func getPrivateContext() -> NSManagedObjectContext {
let bundle = NSBundle.mainBundle()
let modelURL = bundle.URLForResource("MyApp", withExtension: "momd")
let model = NSManagedObjectModel(contentsOfURL: modelURL!)!
let psc = NSPersistentStoreCoordinator(managedObjectModel: model)
let privateContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator = psc
let documentsURL = CoreDataStack.applicationDocumentsDirectory()
let storeURL = documentsURL.URLByAppendingPathComponent("MyApp.sqlite")
let options = [NSMigratePersistentStoresAutomaticallyOption: true]
var error: NSError? = nil
let store: NSPersistentStore?
do {
store = try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options)
} catch let error1 as NSError {
error = error1
store = nil
}
if store == nil {
print("Error adding persistent store: \(error)")
abort()
}
return privateContext
}
class func applicationDocumentsDirectory() -> NSURL {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[0]
}
}
I need to firstly confirm/clarify some points:
A) Is it legal/correct to create a full Core Data stack to use a context in a private queue the way I'm doing?
B) Would creating even a new NSManagedObjectModel from the same resource and same .sqlite file than the one used in the AppDelegate default Core Data stack cause problems?
About managing both contexts I have (the default in AppDelegate, let's call it mainContext, and the one I create in a private queue, let's call it privateContext):
The mainContext is intended to show the NSManagedObject information throughout the app.
The privateContext is intended to be used to call web services to get updated data, create the new NSManagedObject with the received information, and compare this new objects with the ones the app already have.
My questions regarding this are:
Should the privateContext be always used by calling performBlock or performBlockAndWait? Does that include all related operations, such s reading/inserting objects to the privateContext, and clearing/saving it?
The mainContext is supposed to be associated to the main queue/thread, right? So then all its related operations should be performed in main thread...
Having into account that privateContext has its own full Core Data stack... if I save its objects, would they be stored at the same .sqlite file than the ones when saving the mainContext? Or would such file be some way "duplicated"?
If privateContext should save data from its private queue, and mainContext should be used in main thread, would it cause any problem to fetch from the mainContext the data that was saved from the privateContext?
I need help to understand and correctly manage Core Data concurrency in my app, I'm making a mess with all this persistence staff and I'm occasionally finding errors in operations that seemed to work... thanks so much in advance.

Failed finishing setup for store during asynchronous iCloud initialization

I am in the process of creating a core data stack in Swift 2 that handles syncing data with other iDevices using iCloud using different resources (Mostly in Objective-C).
When running the app I sometimes get the following error, and if I run it again the error goes but sometimes displays old data that was supposed to be deleted:
2015-09-08 22:49:31.104 APPNAME[5606:418539]
-[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](874): CoreData: Ubiquity:
nobody~sim73E13D94-AB95-59FB-AF18-ADC7BC05B47B:APPNAMEStore Using local
storage: 1 for new NSFileManager current token <766b5e5c 3c205110
52c05248 38a47bd9 aca1ee87> 2015-09-08 22:49:31.185
APPNAME[5606:418593] -[PFUbiquitySetupAssistant
finishSetupForStore:error:](1125): CoreData: Ubiquity: CoreData:error: Caught Exception Unable to resolve mismatching KVs with
userInfo {
PFUbiquitySetupDoFork = 1; } in -finishSetupForSet:error: 2015-09-08 22:49:31.243 APPNAME[5606:418593] -[PFUbiquitySetupAssistant
finishSetupWithRetry:](829): CoreData: Ubiquity: <PFUbiquitySetupAssistant: 0x7f97b3328320>: Retrying after delay: 60
Error Domain=NSCocoaErrorDomain Code=134080 "(null)" UserInfo={failed finishing setup for store during asynchronous iCloud
initialization=file:///Users/USER/Library/Developer/CoreSimulator/Devices/19DE5FBA-9248-410D-9264-6B434B30F8CA/data/Containers/Data/Application/5B174D0D-C80D-4362-90EE-DC640F907962/Documents/CoreDataUbiquitySupport/nobody~sim73E13D94-AB95-59FB-AF18-ADC7BC05B47B/APPNAMEStore/DD2F75D5-FEAF-482A-A896-657936AFCFCD/store/APPNAME.sqlite}
2015-09-08 22:50:31.288 APPNAME[5606:418966] -[PFUbiquitySetupAssistant finishSetupForStore:error:](1125): CoreData: Ubiquity: CoreData: error: Caught Exception Unable to resolve mismatching KVs with userInfo {
PFUbiquitySetupDoFork = 1; } in -finishSetupForSet:error: 2015-09-08 22:50:31.289 APPNAME[5606:418966]
-[PFUbiquitySetupAssistant
finishSetupWithRetry:](829): CoreData: Ubiquity: <PFUbiquitySetupAssistant: 0x7f97b3328320>: Retrying after delay: 120 Error Domain=NSCocoaErrorDomain Code=134080 "(null)" UserInfo={failed finishing setup for store during asynchronous iCloud
initialization=file:///Users/USER/Library/Developer/CoreSimulator/Devices/19DE5FBA-9248-410D-9264-6B434B30F8CA/data/Containers/Data/Application/5B174D0D-C80D-4362-90EE-DC640F907962/Documents/CoreDataUbiquitySupport/nobody~sim73E13D94-AB95-59FB-AF18-ADC7BC05B47B/APPNAMEStore/DD2F75D5-FEAF-482A-A896-657936AFCFCD/store/APPNAME.sqlite}
2015-09-08 22:52:31.333 APPNAME[5606:420217] -[PFUbiquitySetupAssistant finishSetupForStore:error:](1125): CoreData: Ubiquity: CoreData: error: Caught Exception Unable to resolve mismatching KVs with userInfo {
PFUbiquitySetupDoFork = 1; } in -finishSetupForSet:error:
Core Data Stack
// MARK: - Core Data stack
// This handles the updates to the data via iCLoud updates
func registerCoordinatorForStoreNotifications (coordinator : NSPersistentStoreCoordinator) {
let nc : NSNotificationCenter = NSNotificationCenter.defaultCenter();
nc.addObserver(self, selector: "StoresWillChange:",
name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
object: coordinator)
nc.addObserver(self, selector: "StoresDidChange:",
name: NSPersistentStoreCoordinatorStoresDidChangeNotification,
object: coordinator)
nc.addObserver(self, selector: "StoreChangedUbiquitousContent:",
name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
object: coordinator)
}
// Subscribe to NSPersistentStoreCoordinatorStoresWillChangeNotification
// most likely to be called if the user enables / disables iCloud
// (either globally, or just for your app) or if the user changes
// iCloud accounts.
func StoresWillChange (notification:NSNotification) {
managedObjectContext.performBlock { () -> Void in
if self.managedObjectContext.hasChanges {
do {
try self.managedObjectContext.save()
} catch {
print("error saving Managed Object Context in AppDelegate")
}
} else{
// drop any manged object refrences
self.managedObjectContext.reset()
}
}
}
// Subscribe to NSPersistentStoreCoordinatorStoresDidChangeNotification
func StoresDidChange(notification: NSNotification) {
NSLog("storesDidChange posting notif");
NSNotificationCenter.defaultCenter().postNotificationName("storeDidChange", object: nil)
}
func mergeChanges(notification: NSNotification) {
NSLog("mergeChanges notif:\(notification)")
self.managedObjectContext.performBlock {
self.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
}
}
func StoreChangedUbiquitousContent(notification: NSNotification) {
self.mergeChanges(notification);
}
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.USER.swift2-iCloud" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle().URLForResource("APPNAME", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("APPNAME.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
// iCloud store
var storeOptions = [NSPersistentStoreUbiquitousContentNameKey : "APPNAMEStore",NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
do {
try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: NSURL.fileURLWithPath(url.path!), options: storeOptions)
} catch var error1 as NSError {
error = error1
coordinator = nil
// 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
error = 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 \(error), \(error!.userInfo)")
abort()
} catch {
fatalError()
}
self.registerCoordinatorForStoreNotifications (coordinator!)
return coordinator
}()
Does anybody understand why this is occurring and how I can resolve the error ? I really appreciate feedback as I am hoping to create a public GitHub repository for other new developers to use.
Couldn't leave it as a comment, cuz wrote here. Found reply about this issue here
In terms of those iCloud errors that appear, they generally look a lot scarier than they are. My experience has been that iCloud is great at generating a lot of very scary looking messages in the log that don’t actually signal anything is wrong. In most cases the errors automatically correct themselves after a time. The most intervention I have needed to do at times has been when using the iOS simulator where at times you need to reset the computer to fix it.
However, there are some things that can cause iCloud containers to become corrupted. If you are performing a lot of synchronisation testing, in particular operations that generate a lot of duplicates, it can take an extended time for the iCloud container to properly sync.
You also need to be careful that any, and I do mean any, accessing of the information held in Core Data is handled from the same thread. That includes reading from the objects that you have loaded from Core Data. Accessing the information from multiple threads can very easily cause corruption that does not manifest until you try to sync the container (or migrate it). The best way to avoid that is to use the performBlock and performBlockAndWait methods on the iCloud + Core Data stack. Those methods guarantee that the operations (read or write) will happen on the same thread to prevent data corruption.

Resources