Getting an error when saving a second managed object to core data

Hi everyone!

I am working on a coin collection app, and I have recently started integrating core data into my app.

My app is of the following format: I have a core data database which stores custom CoinCategory objects and each CoinCategory object has an array of Coin objects as a member. This way, my app stores everything in terms of categories, which makes it easier to sort the coins in the collection.

After I integrated the core data, I can add my first category without any errors and delete it without any problems, but when I add in a second coin category, I experience the following error:


"

2017-06-26 09:55:37.218 CoinCollection[18889:12839563] -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 2017-06-26 09:55:37.219215-0400 CoinCollection[18889:12839563] [error] error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null) CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null) 2017-06-26 09:55:37.242 CoinCollection[18889:12839563] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0'

"


OK. I have been working on this for several days now and I have read a couple of stackoverflow threads on core data. Apparently some writers believe that there is an issue with multithreading and that people think that they should create an NSManagedContext object with concurrency type being

.PrivateQueueConcurrencyType
as shown in this thread: https://stackoverflow.com/questions/33562842/swift-coredata-error-serious-application-error-exception-was-caught-during-co


However, I have not done any multithreading explicitly in my application, and the fact that I am able to add my first category to the coin collection app's core data, and then I experience an error when I am adding the second one makes me think that this might not be the case.

Could anyone please advise me on how to fix this error? I am sure that there are other programmers who have also faced core data frustrations and would be interested in seeing how this problem could get resolved.

I am attaching my code below for my viewcontroller and the app delegate that is running the core data. I can also show my code for the CoinCategory and Coin objects that are stored in CoreData.


//AppDelegate.swift
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        UINavigationBar.appearance().barTintColor = UIColor(red: 0.9882, green: 0.5765, blue: 0, alpha: 1.0)
        UINavigationBar.appearance().tintColor = UIColor.white
    
        if let barFont = UIFont(name: "HelveticaNeue-Bold", size: 25.0) {
            UINavigationBar.appearance().titleTextAttributes =
                [NSForegroundColorAttributeName:UIColor.white, NSFontAttributeName:barFont]
        }
    
        /
        /
        UIApplication.shared.statusBarStyle = .lightContent
        return true
    }
    func applicationWillResignActive(_ application: UIApplication) {
        //
    }
    func applicationDidEnterBackground(_ application: UIApplication) {
        //
    }
    func applicationWillEnterForeground(_ application: UIApplication) {
        //
    }
    func applicationDidBecomeActive(_ application: UIApplication) {
        ///
    }
    func applicationWillTerminate(_ application: UIApplication) {
        ///
    }

    /
    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: "CoinCollection")
        container.loadPersistentStores(completionHandler: { (storeDescription,
            error) in
            if let error = error as NSError? {
                /*
                 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
    }()


    func saveContext ()
    {
        let context = persistentContainer.viewContext
    
        if context.hasChanges
        {
            do
            {
                try context.save()
            }
            catch
            {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}


And then for my view controller. Notice that I get a Coin object from a sub-viewcontroller that invokes this method, and that we decide whether the Coin object fits into the existing categories. If not, then we add the new Coin.


Note that I am going to replace all the extra code that does not directly pertain to the core data issue (100 percent) with a comment that summarizes what is going on.


//CoinTableViewController.swift
import UIKit
import CoreData

class CoinTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
    @IBOutlet var navItem: UINavigationItem!
  
    var coinsByCategory: [CoinCategoryMO] = []
    var fetchResultController: NSFetchedResultsController<CoinCategoryMO>!
  
    private var cancelButton: UIBarButtonItem!
    private var editButton: UIBarButtonItem!
  

    private var collectionEmptyLabel: UILabel!
  

    override func viewDidLoad()
    {
        super.viewDidLoad()
      
        //we load the data
        let fetchRequest : NSFetchRequest<CoinCategoryMO> = CoinCategoryMO.fetchRequest()
      
        if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
        {
            let context = appDelegate.persistentContainer.viewContext
          
            let sortDescriptor = NSSortDescriptor(key: "coinCategory", ascending: true)
            fetchRequest.sortDescriptors = [sortDescriptor]
          
            fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
            fetchResultController.delegate = self
          
            do
            {
                try fetchResultController.performFetch()
                if let fetchedObjects = fetchResultController.fetchedObjects
                {
                    self.coinsByCategory = fetchedObjects
                }
            }
            catch
            {
                print(error)
            }
        }
      
        //after loading the data, we create the various graphical objects, such as barbuttons, and we customize the navigation bar
    }
    override func viewWillAppear(_ animated: Bool)
    {
        super.viewWillAppear(animated)
        collapseNavControllerIfNeeded()
    }

     /* And we have a lot of code dealing with other parts of the viewcontroller that are not concerned with coredata whatsoever*/
  
  
    func addCoin(coinToAdd: Coin)
    {
        //this function is typically invoked bya  sub-view-controller that sends us the parameter coinToAdd
       //we first check if it can be added to an existing category, and if not, we create a new one
        for category in self.coinsByCategory
        {
            if category.coinCategory?.coinFitsCategory(aCoin: coinToAdd) == true
            {
                
                category.coinCategory?.addCoin(newCoin: coinToAdd)
              
                if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
                {
                    
                    appDelegate.saveContext()
                  
                    
                    return
                }
            }
        }
      
        
        if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
        {
            let newCategory = CoinCategoryMO(context: appDelegate.persistentContainer.viewContext)
          
            newCategory.coinCategory = CoinCategory(coins: [coinToAdd], categoryType: CoinCategory.CategoryTypes.COUNTRY_VALUE_AND_CURRENCY)
          
            print("Saving data to context ...")
            appDelegate.saveContext()
        }
    }
  
    
  
    
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
    {
        tableView.beginUpdates()
    }
  
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
    {
        switch type
        {
        case .insert :
            if let newIndexPath = newIndexPath
            {
                tableView.insertRows(at: [newIndexPath], with: .fade)
            }
          
        case .delete:
            if let indexPath = indexPath
            {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
              
        case .update:
            if let indexPath = indexPath
            {
                tableView.reloadRows(at: [indexPath], with: .fade)
            }
          
        default:
            tableView.reloadData()
        }
      
        if let fetchedObjects = controller.fetchedObjects
        {
            self.coinsByCategory = fetchedObjects as! [CoinCategoryMO]
        }
    }
  
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
    {
        tableView.endUpdates()
      
    }
}


So given the error messages and code, why is it that when I add the first coin object, which leads to the creation of the first coin category in core data (as there were no categories currently in existence), I did not get an error, but when I add the second coin object, which is in a different category than the first, I have a massive error? [I can provide you with any code that you may need if you need more information]


What could I be doing wrong?

Thanks for reading this post and in advance for the help!

I have seen this happen when the key in a fetchResult sortDescriptor or predicate, does not match any of the entity properties within coreData… this may be the cause forunrecognized selector sent to instance 0x608000236cc0 with userInfo

—————————

This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null) CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null) 2017-06-26 09:55:37.242 CoinCollection[18889:12839563] ***

Ex in CoinTableViewController.swift

  1. let context = appDelegate.persistentContainer.viewContext
  2. let sortDescriptor = NSSortDescriptor(key: "coinCategory", ascending: true)
  3. fetchRequest.sortDescriptors = [sortDescriptor]
  4. fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
  5. fetchResultController.delegate = self

Why are you trying to store managed objects in arrays instead of just using the to-many relationship mechanism?

This saved my bacon. Thank you.

Yep, I was having this same problem and figured out from the above post that it was a problem with my sort descriptor. Thanks so much kpicart!
Getting an error when saving a second managed object to core data
 
 
Q