-1

Okay, so I've done a ton of searching and everything I'm finding seems to have to do with View Controllers and the links on them not working or not being connected. That is not my problem. I have an app that's using CoreData to keep tabs on a list of Tables, their Table Numbers, if they're clean or not, and if they're in use or not. However, when I try to change the key value of an attribute using the setValue() function, it's crashing my app saying the following crash report

2018-04-02 21:45:15.840265-0500 Trakr[26636:1455153] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSManagedObjectContext 0x6040001c4fb0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key inUse.'
*** First throw call stack:
(
0   CoreFoundation                      0x000000010d1af1e6 __exceptionPreprocess + 294
1   libobjc.A.dylib                     0x0000000108f3d031 objc_exception_throw + 48
2   CoreFoundation                      0x000000010d1af0b9 -[NSException raise] + 9
3   Foundation                          0x000000010895eb47 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 292
4   Trakr                               0x0000000108615bee _T05Trakr24CheckTableViewControllerC18inUseSwitchChangedyypF + 846
5   Trakr                               0x0000000108615e41 _T05Trakr24CheckTableViewControllerC18inUseSwitchChangedyypFTo + 81
6   UIKit                               0x000000010ac1a448 -[UIApplication sendAction:to:from:forEvent:] + 83
7   UIKit                               0x000000010ad95804 -[UIControl sendAction:to:forEvent:] + 67
8   UIKit                               0x000000010ad95b21 -[UIControl _sendActionsForEvents:withEvent:] + 450
9   UIKit                               0x000000010b8d6d60 -[UISwitchModernVisualElement sendStateChangeActions] + 73
10  UIKit                               0x000000010b29889f -[UISwitchMVEGestureTrackingSession _sendStateChangeActionsIfNecessary] + 63
11  UIKit                               0x000000010b8d74ec -[UISwitchModernVisualElement _handleLongPressWithGestureLocationInBounds:gestureState:] + 760
12  UIKit                               0x000000010b21141b -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 57
13  UIKit                               0x000000010b21a1f0 _UIGestureRecognizerSendTargetActions + 109
14  UIKit                               0x000000010b217a38 _UIGestureRecognizerSendActions + 307
15  UIKit                               0x000000010b216c8c -[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 859
16  UIKit                               0x000000010b1fbcf0 _UIGestureEnvironmentUpdate + 1329
17  UIKit                               0x000000010b1fb773 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 484
18  UIKit                               0x000000010b1fa875 -[UIGestureEnvironment _updateGesturesForEvent:window:] + 281
19  UIKit                               0x000000010ac9080b -[UIWindow sendEvent:] + 4064
20  UIKit                               0x000000010ac34370 -[UIApplication sendEvent:] + 352
21  UIKit                               0x000000010b57557f __dispatchPreprocessedEventFromEventQueue + 2796
22  UIKit                               0x000000010b578194 __handleEventQueueInternal + 5949
23  CoreFoundation                      0x000000010d151bb1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
24  CoreFoundation                      0x000000010d1364af __CFRunLoopDoSources0 + 271
25  CoreFoundation                      0x000000010d135a6f __CFRunLoopRun + 1263
26  CoreFoundation                      0x000000010d13530b CFRunLoopRunSpecific + 635
27  GraphicsServices                    0x000000010fa46a73 GSEventRunModal + 62
28  UIKit                               0x000000010ac190b7 UIApplicationMain + 159
29  Trakr                               0x000000010861e697 main + 55
30  libdyld.dylib                       0x000000010e318955 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

I cannot for the life of me figure out why this is happening. Here is my view controller class:

import UIKit
import CoreData

class CheckTableViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

@IBOutlet weak var backgroundImage: UIImageView!
@IBOutlet weak var pickerView: UIPickerView!
@IBOutlet weak var tableNumberLabel: UILabel!
@IBOutlet weak var cleanSwitch: UISwitch!
@IBOutlet weak var inUseSwitch: UISwitch!


var tablesList = [Table]()

override func viewDidLoad() {
    super.viewDidLoad()
    pickerView.delegate = self
    pickerView.dataSource = self

    let fetchRequest: NSFetchRequest<Table> = Table.fetchRequest()

    do {
        let tablesList = try PersistenceService.context.fetch(fetchRequest)
        self.tablesList = tablesList
    } catch {}

    backgroundImage.addBlurEffect()
    tableNumberLabel.text = "Select a Table"
    cleanSwitch.isEnabled = false
    inUseSwitch.isEnabled = false

    if tablesList.isEmpty {
        pickerView.isHidden = true
        tableNumberLabel.text = "Add Some Tables"
    } else {
        pickerView.isHidden = false
    }

    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Picker View and Switch Controlls
func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return tablesList.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return String(tablesList[row].tableNumber)
}


func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if (tablesList.isEmpty) {
        tableNumberLabel.text = "Add a table"
    } else {
        tableNumberLabel.text = String(tablesList[row].tableNumber)
        if cleanSwitch.isEnabled == false {
            cleanSwitch.isEnabled = true
        }

        if inUseSwitch.isEnabled == false {
            inUseSwitch.isEnabled = true
        }

        if tablesList[row].isClean == true {
            cleanSwitch.isOn = true
        } else {
            cleanSwitch.isOn = false
        }

        if tablesList[row].inUse == true{
            inUseSwitch.isOn = true
        } else {
            inUseSwitch.isOn = false
        }
    }
}

@IBAction func cleanSwitchChanged(_ sender: Any) {
    if cleanSwitch.isOn == false {
        PersistenceService.context.setValue(false, forKey: "isClean")
    } else {
        PersistenceService.context.setValue(true, forKey: "isClean")
    }
}

@IBAction func inUseSwitchChanged(_ sender: Any) {
    if inUseSwitch.isOn == false {
        let selectedRow = pickerView.selectedRow(inComponent: 0)
        tablesList[selectedRow].inUse = false
        PersistenceService.context.setValue(false, forKey: "inUse")

    } else {
        let selectedRow = pickerView.selectedRow(inComponent: 0)
        tablesList[selectedRow].inUse = true
        PersistenceService.context.setValue(true, forKey: "inUse")
    }
}

}

And here is my PersistenceService class, the one responsible for CoreData functionality

import Foundation
import CoreData

class PersistenceService {
// MARK: - Core Data stack

private init() {}

static var context: NSManagedObjectContext {
    return persistentContainer.viewContext
}

static 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: "tableModel")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() 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.

            /*
             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
}()

// MARK: - Core Data Saving support

static func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
            print("Saved to CoreData")
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() 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
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}
}

And finally, a screenshot of my tableModel CoreData Model

Can someone tell me why this isn't working? I can't understand it for the life of me....

rmaddy
  • 314,917
  • 42
  • 532
  • 579
TerrorByte
  • 49
  • 5
  • This should be `entity instance` instead of `NSManagedObjectContext instance` where you are setting the values. – TheTiger Apr 03 '18 at 05:27

1 Answers1

0

Instead of using KVC, you should create Table subclass of NSManagedObject. Then you can set the value directly in "Table" instance.

Also you are setting value as context.setValue(:), you should create an entityDescription of Table first like below

var newTable = NSEntityDescription.insertNewObject(forEntityName: "Table", into: PersistenceService.context)
newTable.setValue(fale, forKey: "isClean")
Boudhayan
  • 750
  • 4
  • 15
  • Thank you! However, I've also discovered that calling PersistenceService.saveContext() is actually saving the data, even though, as far as I know, I haven't modified the context. How is this happening? – TerrorByte Apr 03 '18 at 04:05