6

In my closure of my network request, I am inserting objects into core data using a private concurrency queue and getting a crash when I call "perform" on the private context.

Crash message in console:

libc++abi.dylib: terminating with uncaught exception of type NSException

Stack Trace: enter image description here

The code that's causing the crash:

API.sync(onlyMe, syncToken: syncToken) { success, syncResponse in CoreDataUtils.privateContext.perform { // crashes on this line .... } } 

My Core Data stack (unfortunately located in the AppDelegate at the moment and not in a CoreDataStack class):

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 var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite") let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] do { try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options) } catch { print(error) } return coordinator }() lazy var privateManagedObjectContext: NSManagedObjectContext = { // Initialize Managed Object Context var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) // Configure Managed Object Context managedObjectContext.parent = self.managedObjectContext return managedObjectContext }() 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. var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator managedObjectContext.mergePolicy = NSRollbackMergePolicy //This policy discards in-memory state changes for objects in conflict. The persistent store’s version of the objects’ state is used return managedObjectContext }() 

Where CoreDataUtils.privateContext is:

class CoreDataUtils: NSObject { static let appDel = UIApplication.shared.delegate as! AppDelegate static let context: NSManagedObjectContext { return appDel.managedObjectContext } static let privateContext: NSManagedObjectContext { appDel.privateManagedObjectContext } } 
3
  • Please post a stack trace, or some sort of error message. From the title it looks like a concurrency issue. Commented May 18, 2018 at 17:46
  • @DanBeaulieu Made the edit! Commented May 18, 2018 at 17:52
  • 2
    This question may be helpful > stackoverflow.com/questions/31391838/… Commented May 18, 2018 at 18:13

1 Answer 1

1

I followed @CodeBender's link and found that there's several locations where I am getting a a multithreading violation. The link provides a way to debug concurrency issues by passing arguments in your project's scheme. This method provides better detail on what went wrong and where.

For example I have a function that performs a fetch, and I did not wrap the code in a perform block. This is how I implemented a fix:

static func searchObject(in context: NSManagedObjectContext = context, _ entity: String, key: String, value: Any) -> [NSManagedObject]? { var objects: [NSManagedObject]? context.performAndWait { //searching core data for a specific attribute within an identity via a predicate let request = NSFetchRequest<NSFetchRequestResult>(entityName: "\(entity)") request.returnsObjectsAsFaults = false request.predicate = NSPredicate(format: "\(key) == %@", "\(value)") do { let results = try context.fetch(request) objects = results as? [NSManagedObject] } catch { let nserror = error as NSError Bugsnag.notifyError(nserror) NSLog("Unresolved error \(nserror), \(nserror.userInfo)") abort() } } return objects } 

Also, in another spot where I am inserting Notification objects from the server, I was calling the perform block but I was not instantiating the managed objects with the proper context.

CoreDataUtils.privateContext.perform { for notification in notifications { // BEFORE (WRONG) - would default to the main context (CoreDataUtils.context - see question) //notification.insert() // NOW (CORRECT) - inserts into private queue we are performing on notification.insert(into: CoreDataUtils.privateContext) } CoreDataUtils.save() } 

Where in the Notification model I have:

func insert(into context: NSManagedObjectContext = CoreDataUtils.context) -> NotificationObject { assert(CoreDataUtils.searchObject(Models.notification, key: "id", value: self.id) == nil) let newNotification = NotificationObject(notification: self, context: managedObjectContext) return newNotification } 
Sign up to request clarification or add additional context in comments.

2 Comments

So thinking in the context of the question you asked. Do you think you could provide a clear solution to that problem here? For example, perhaps share what you changed in your code to make it compile? Remember that your contribution here will help future developers for years to come. :)
@DanBeaulieu Done ✔

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.