Reputation: 1058
I am writing one program on iOS and very race I am facing this error:
2015-11-06 10:57:24.289 NETFNET[2503:976392] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null) 2015-11-06 10:57:24.293 NETFNET[2503:976392] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
I am trying to access Data Base simultaneously, I think, from main and background threads. I have seen a lot of solutions for Objective C, but none for Swift (I don't know Objective C...). Unfortunately, I don't know how to work with Grand Central Dispatch and, in fact, my program does not really need several treads (I mean it need it, but if some thread loses info from one function for one time, nothing bad will happen). I just want to have stable program on Swift 1 or 2, so I will be thankful for any help.
Upvotes: 6
Views: 7067
Reputation: 429
Just calling your CoreData Function inside
DispatchQueue.main.async {
...
}
worked for me
Upvotes: 3
Reputation: 4202
You can create an extension for this and wrap the save()
function to something like this so you'll just need to use this function instead of save()
:
extension NSManagedObjectContext {
func update() throws {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = self
context.perform({
do {
try context.save()
} catch {
print(error)
}
})
}
}
Upvotes: 1
Reputation: 626
Very good. I tried it, it worked fine for me. Thank you very much.
Previous Code:
do {
try CDHelper.shared.context.save()
}
catch let error as NSError {
// Error mesajlarını ekle!!
print("Could not fetch \(error), \(error.localizedDescription)")
print("Could not fetch \(error), \(error.localizedFailureReason)")
}
// MARK: - CONTEXT
lazy var context: NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)
moc.persistentStoreCoordinator = self.coordinator
return moc
}()
// MARK: - MODEL
lazy var model: NSManagedObjectModel = {
return NSManagedObjectModel(contentsOfURL:self.modelURL)!
}()
// MARK: - COORDINATOR
lazy var coordinator: NSPersistentStoreCoordinator = {
return NSPersistentStoreCoordinator(managedObjectModel:self.model)
}()
lazy var modelURL: NSURL = {
let bundle = NSBundle.mainBundle()
if let url = bundle.URLForResource("Model", withExtension: "momd") {
return url
}
print("CRITICAL - Managed Object Model file not found")
abort()
}()
you should change the code this way:
let moc = NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock({
do {
try privateMOC.save()
} catch {
fatalError("Failure to save context: \(error)")
}
})
Upvotes: 6
Reputation: 1396
You need to create a private NSManagedObjectContext with private queue concurrency type and use it to access CoreData whenever operating on a background thread.
So suppose I need to run a database operation on the background, I can dispatch that work to the background thread as
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
//call your background operation.
})
Then in the background operation I can create a private NSManagedObjectContext as
let moc = … //Our primary context on the main queue
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock {
//operations
do {
try privateMOC.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}
Read through Apple's CoreData Concurrency Guide to get a good understanding before implementing core data operations on multiple threads.
Upvotes: 7