Thermostat Model Key Path Issues


#1

When I set the model key path for the vertical slider to the temperature variable, I see the error message “The value binding expects to be bound to an object of type NSNumber, but temperature is of type int”. So I change the temperature definition to "var temperature = 68 as NSNumber. This takes care of the first error.

Now when I set the model key path for the label I get the error message “The value binding expects to be bound to an object of type NSString, but temperature is of type NSNumber” It would appear that I want something other than a Label to display the value of the temperature variable. I have deleted the label and added a text field with number formatter but now Xcode just hangs. Very perplexing.

Did anyone else have this sort of issue?


#2

Xcode 9 (swift 4) requires @objc declaration on variables in order to use KVC.

@objc var temperature = 68


#3

This is not working with XCode 9.4. I’m getting the following exception when I try to launch the app:

2018-08-11 20:07:34.183578+0530 Thermostat[3249:92447] *** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<NSApplication 0x60c000101dd0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key temperature.’
*** First throw call stack:
(
0 CoreFoundation 0x00007fff2d2b632b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00007fff54934c76 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2d2b6279 -[NSException raise] + 9
3 Foundation 0x00007fff2f46c2bf -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 226
4 Foundation 0x00007fff2f33f04c -[NSObject(NSKeyValueCoding) valueForKey:] + 284
5 AppKit 0x00007fff2a98da83 -[NSApplication(NSScripting) valueForKey:] + 499
6 AppKit 0x00007fff2a8274ab -[NSBinder valueForBinding:resolveMarkersToPlaceholders:] + 162
7 AppKit 0x00007fff2a8c0e3f -[NSValueBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] + 226
8 AppKit 0x00007fff2a8c0cd0 -[NSValueBinder _observeValueForKeyPath:ofObject:context:] + 277
9 AppKit 0x00007fff2a81f8db -[NSObject(NSKeyValueBindingCreation) bind:toObject:withKeyPath:options:] + 782
10 AppKit 0x00007fff2a79b719 -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] + 1430
11 AppKit 0x00007fff2a792991 loadNib + 435
12 AppKit 0x00007fff2a791eb5 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:options:withZone:ownerBundle:] + 696
13 AppKit 0x00007fff2a791afa -[NSBundle(NSNibLoading) loadNibNamed:owner:topLevelObjects:] + 204
14 AppKit 0x00007fff2a7918bb +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 447
15 AppKit 0x00007fff2a78f946 NSApplicationMain + 504
16 Thermostat 0x0000000100002abd main + 13
17 libdyld.dylib 0x00007fff5554e015 start + 1
18 ??? 0x0000000000000003 0x0 + 3
)
libc++abi.dylib: terminating with uncaught exception of type NSException


#4

Did you add @objc dynamic to your variables? like this:

@objc dynamic var temperature: Int {
	set {
		print("set temperature to \(newValue)")
		privateTemperature = newValue
	}
	get {
		print("get temperature")
		return privateTemperature
	}
}

@objc dynamic var isOn = 1  // have to use the number 1 because this is an Objective-C property

#5

I have specified property according to early post.

var privateTemperature : Int = 68
@objc dynamic var temperature : Int {
get { return privateTemperature}
set { privateTemperature = newValue}
}

But still I’m getting the same error in the below statement : class AppDelegate: NSObject, NSApplicationDelegate {.

2018-08-12 10:27:05.833086+0530 Thermostat[1401:20302] *** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<NSApplication 0x60c000100870> valueForUndefinedKey:]: this class is not key value coding-compliant for the key temperature.’
*** First throw call stack:
(
0 CoreFoundation 0x00007fff5358f32b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00007fff7ac0dc76 objc_exception_throw + 48
2 CoreFoundation 0x00007fff5358f279 -[NSException raise] + 9
3 Foundation 0x00007fff557452bf -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 226
4 Foundation 0x00007fff5561804c -[NSObject(NSKeyValueCoding) valueForKey:] + 284
5 AppKit 0x00007fff50c66a83 -[NSApplication(NSScripting) valueForKey:] + 499
6 AppKit 0x00007fff50b004ab -[NSBinder valueForBinding:resolveMarkersToPlaceholders:] + 162
7 AppKit 0x00007fff50b99e3f -[NSValueBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] + 226
8 AppKit 0x00007fff50b99cd0 -[NSValueBinder _observeValueForKeyPath:ofObject:context:] + 277
9 AppKit 0x00007fff50af88db -[NSObject(NSKeyValueBindingCreation) bind:toObject:withKeyPath:options:] + 782
10 AppKit 0x00007fff50a74719 -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] + 1430
11 AppKit 0x00007fff50a6b991 loadNib + 435
12 AppKit 0x00007fff50a6aeb5 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:options:withZone:ownerBundle:] + 696
13 AppKit 0x00007fff50a6aafa -[NSBundle(NSNibLoading) loadNibNamed:owner:topLevelObjects:] + 204
14 AppKit 0x00007fff50a6a8bb +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 447
15 AppKit 0x00007fff50a68946 NSApplicationMain + 504
16 Thermostat 0x0000000100002a5d main + 13
17 libdyld.dylib 0x00007fff7b827015 start + 1
18 ??? 0x0000000000000003 0x0 + 3
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)


#6

Sorry I know my responses are slow sometimes. I’ll try to take a look at this tomorrow.


#7

As a side note for your definition:

you don’t need to define privateTemperature with the Int type. Swift will infer the type from this:

var privateTemperature = 68

No huge deal, just pointing it out, and this is definitely NOT causing your issue.

When you first built the program, were you able to launch the empty window before adding any Thermostat code? Meaning, do you have the single window set up and the new windowNibName?

If so, did the program run initially when you set up the bindings between the slider and the label with File’sOwner.temperature? I’m assuming so. (I’m just trying to work through where you are in the process and what worked, and what was added when it quit running. I am new to programming so I have to work through it)

Try reviewing all of your bindings per the Debugging Bindings section of this chapter. If you are still stuck, the next advice I have is to completely build it again from the beginning. A lot of us on here have had to do that. Seems like there are times where Xcode puts a bug in as code is changed, changed again, copied and pasted. I built it again from the beginning while reviewing the chapter to see what might be causing your issue, it won’t take too long.

It seems to me the issue is with the bindings somehow. You could try rebinding to file’s owner and temperature to see if that works, before starting over.


#8

Yes. Single window appears correctly. This error occurs only when I bind temperature with slider/label controls.

I’m using latest xcode 9.4.1


#9

I too am using Xcode 9.4.1.

Here is my MainWindowController.swift:

import Cocoa

class MainWindowController: NSWindowController {

private var privateTemperature = 68
@objc dynamic var isOn = true
@objc dynamic var temperature: Int {
	set {
		privateTemperature = newValue
	}
	get {
		return privateTemperature
	}
}

@IBAction func makeWarmer(sender: NSButton) {
	temperature = temperature + 1
}

@IBAction func makeCooler(sender: NSButton) {
	temperature = temperature - 1
}


    // The following is not needed, I included it incase you did to see if it caused the error somehow.
    //  It didn't.
override func setNilValueForKey(_ key: String) {
	switch key {
	case "temperature":
		temperature = 68
	default:
		super.setNilValueForKey(key)
	}
}


override var windowNibName: NSNib.Name? {
	return NSNib.Name(rawValue: "MainWindowController")
}

override func windowDidLoad() {
    super.windowDidLoad()

    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  }

}

If yours is like this one, then the problem has to be with the bindings.

Open MainWindowController.xib and right click on File’s Owner. It should be similar to:

Click on each of the follow and check the bindings:
vertical slider and label binding:

Button Bindings:

If all your bindings look like these, then try deselecting all bindings and redoing them.

If that doesn’t work, start completely over and see if it works that time.