Ios – Core Data crash attempt to insert nil with userInfo (null)

asynchronouscore-dataiosswift

When my code tries to download the series from the API, it randomly crashes with this error message:

(entity: Series; id:
0x7b181002016-04-04 14:01:33.868 Postzegel Catalogus[1816:39059]
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) 0 (entity: Series; id:
0x7b00c450
;
2016-04-04 14:01:33.871 Postzegel Catalogus[1816:39059] *** Terminating app due
to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack: ( 0 CoreFoundation 0x0083d494 __exceptionPreprocess + 180 1 libobjc.A.dylib
0x02551e02 objc_exception_throw + 50 2 CoreFoundation
0x0083d3bd +[NSException raise:format:] + 141 3 CoreFoundation
0x0070c959 -[__NSCFSet addObject:] + 185 4 CoreData
0x0038a010 -[NSManagedObjectContext(_NSInternalChangeProcessing)
_processPendingInsertions:withDeletions:withUpdates:] + 560 5 CoreData 0x003846da
-[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2410 6 CoreData 0x00383d56 -[NSManagedObjectContext processPendingChanges] + 54 7
CoreData 0x003ae5e4
-[NSManagedObjectContext(_NestedContextSupport) _parentProcessSaveRequest:inContext:error:] + 116 8 CoreData 0x00433bec __82-[NSManagedObjectContext(_NestedContextSupport)
executeRequest:withContext:error:]_block_invoke + 412 9 CoreData
0x003a924c internalBlockToNSManagedObjectContextPerform + 76 10
libdispatch.dylib 0x03c8f9cd
_dispatch_client_callout + 14 11 libdispatch.dylib 0x03c76d90 _dispatch_barrier_sync_f_slow_invoke + 133 12
libdispatch.dylib 0x03c8f9cd
_dispatch_client_callout + 14 13 libdispatch.dylib 0x03c74f7c _dispatch_main_queue_callback_4CF + 910 14 CoreFoundation
0x007871be __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
+ 14 15
CoreFoundation 0x00745434 __CFRunLoopRun + 2356
16 CoreFoundation 0x00744846
CFRunLoopRunSpecific + 470 17 CoreFoundation
0x0074465b CFRunLoopRunInMode + 123 18 GraphicsServices
0x07a8d664 GSEventRunModal + 192 19 GraphicsServices
0x07a8d4a1 GSEventRun + 104 20 UIKit
0x0102beb9 UIApplicationMain + 160 21 Postzegel Catalogus
0x000f63b1 main + 145 22 libdyld.dylib
0x03cb9a25 start + 1 ) (entity: Series; id: 0x7b25bc40
;

And I just don't know why? I used a private Managed Context Option so that using a . I even set the fields in my .xcdatamodeld to optional. So it shouldn't be a problem it it is nil? And it just keeps crashing randomly, not even at the same object. How can I fix this?

I included my code in the hope that might help you. I have removed my API key, so you won't be able to try it out. If you have any other comment on my code please tell me, I'm new to Core Data and Alamofire so I have the tenancy to make 'spaghetti code'.

Thanks in advance

import Foundation
import CoreData
import Alamofire
import SwiftyJSON

//CoreData Init
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext: NSManagedObjectContext = appDelegate.managedObjectContext

let queue = dispatch_queue_create("com.GJ-Computers.Postzegel-Catalogus.responseJSON-Manager", DISPATCH_QUEUE_CONCURRENT)

//Colnect API
let LANG: String = NSLocale.preferredLanguages()[0].substringToIndex(NSLocale.preferredLanguages()[0].startIndex.advancedBy(2))
let DATE = NSCalendar.currentCalendar().component([.Day, .Month, .Year], fromDate: NSDate())
let API_KEY: String = "----" //Private API KEY
let CAT_STAMPS: String = ("cat/stamps/")
var BASE_URL: String{
    return ("http://api.colnect.net/" + LANG + "/api/" + API_KEY + "/")
}

//Ghetto Delegate
var didGetCountires: Bool = false
var didGetYears: Bool = false
var didGetSeries: Bool = false

//MARK: - First Time setup Database
func setupDatabase(){
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)){


        getYears() //Download Years per country from database        
}
    }


//get Series
func getSeries(){
    //Retrieve Countries from Coredata
    let countryFetchRequest = NSFetchRequest(entityName: "Countries")
    var results: [Countries]?
    do {
        results = try managedContext.executeFetchRequest(countryFetchRequest) as? [Countries]
    } catch let error as NSError {
        print("Could not fetch \(error), \(error.userInfo)")
    }

    let resultCount = results!.count
    var completedRequestCount: Int = 0
    var requestedRequests = 0


    for result in results!{
        let countryID = result.countryID
        Alamofire.request(.GET, (BASE_URL + "series/"+CAT_STAMPS+"producer/\(countryID)")).responseJSON(queue: queue, completionHandler:{ response in
            if let json = response.result.value{
                let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
                privateMOC.parentContext = managedContext

                let rawData = JSON(json)
                for data in rawData {
                    //Setup let
                    let seriesID = Int(data.1.array![0].string!)
                    let seriesName = data.1.array![1].string
                    let itemCount = Int(data.1.array![2].string!)

                    if seriesID != 0 && itemCount != 0 && seriesName != nil{
                        privateMOC.performBlock{
                            let series = NSEntityDescription.insertNewObjectForEntityForName("Series", inManagedObjectContext: managedContext) as! Series
                            series.countryID = countryID
                            series.seriesID = seriesID
                            series.seriesName = seriesName
                            series.itemCount = itemCount

                            print(completedRequestCount)

                            do {
                                try privateMOC.save()
                            } catch let error as NSError  {
                                print("Could not save \(error), \(error.userInfo)")
                            }
                        }

                    }else{
                        print("ERROR")
                    }

                }
            }
            completedRequestCount += 1
            print(completedRequestCount)
        })
        requestedRequests += 1
        if(requestedRequests == resultCount){
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)){
                while(true){
                    if(completedRequestCount == resultCount){
                        didGetSeries = true
                        sleep(3)
                        print("DEBUG - Series Done")
                        break
                    }
                }
            }
        }
    }
}

Best Answer

Actually, the reason, using private MOC solved your problem is because you are performing the operation in the background thread, and when you do coredata operation in the background thread you have to ensure that

  • Managed object contexts are bound to the thread (queue) that they are associated with upon initialization
  • Managed objects retrieved from a context are bound to the same queue that the context is bound to

FYI not because of ("I made a private MOC, but when you make one you have to consistently add it in the code otherwise it won't work. I forgot to replace managedContext with privateMOC")

Related Topic