Search code examples
macoscocoaswift4nstextfieldkvc

NSTextField bound to double in Swift, how to allow blank?


I'm using a NSTextField which I have bound to an integer via a binding in the storyboard. The Swift code looks like this:

@objc dynamic var quantity: Int = 0

This works great for validating numbers and I get a helpful message if there is an attempt to enter something invalid, for example alphabetic letters.

The problem I'm having is that the application is crashing when you tab out of the field without filling anything in. Following the error messages, the most relevant appears to be:

2019-03-09 16:29:46.813928+0000 ViewWebSocketLearning[34535:1969855] [General] [<ViewWebSocketLearning.OrderFormViewController 0x103131820> setNilValueForKey]: could not set nil as the value for the key show.
2019-03-09 16:29:46.825146+0000 ViewWebSocketLearning[34535:1969855] [General] (
    0   CoreFoundation                      0x00007fff3d7b6ded __exceptionPreprocess + 256
    1   libobjc.A.dylib                     0x00007fff69882720 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff3d7b6c1f +[NSException raise:format:] + 201
    3   Foundation                          0x00007fff3fbe8dbb -[NSObject(NSKeyValueCoding) setNilValueForKey:] + 81
    4   Foundation                          0x00007fff3fac1450 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 331
    5   Foundation                          0x00007fff3faec38a -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 271
    6   AppKit                              0x00007fff3af0bf03 -[NSBinder _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:] + 473
    7   AppKit                              0x00007fff3af0bccf -[NSBinder setValue:forBinding:error:] + 236
    8   AppKit                              0x00007fff3b54c376 -[NSValueBinder _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:] + 225
    9   AppKit                              0x00007fff3b54c698 -[NSValueBinder applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:] + 544
    10  AppKit                              0x00007fff3b54c81a -[NSValueBinder _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:] + 105
    11  AppKit                              0x00007fff3b053e4e -[NSValueBinder validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 460
    12  AppKit                              0x00007fff3b053c5a -[_NSBindingAdaptor _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor:] + 175
    13  AppKit                              0x00007fff3b053b8d -[_NSBindingAdaptor validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 240
    14  AppKit                              0x00007fff3af83e2b -[NSTextField textShouldEndEditing:] + 368
    15  AppKit                              0x00007fff3af44cc9 -[NSTextView(NSSharing) resignFirstResponder] + 499
    16  AppKit                              0x00007fff3ada2522 -[NSWindow _realMakeFirstResponder:] + 258
    17  AppKit                              0x00007fff3b058001 -[NSTextView(NSPrivate) _giveUpFirstResponder:] + 263

I am interpreting this as the NSTextField binding not accepting a nil entry, apparently caused by leaving the field blank.

How can I prevent this exception from occurring? For my application, it's perfectly fine to leave the field blank while working on something else.


Solution

  • By using NSTextFields with bindings Cocoa uses the key-value coding methods (KVC) such as setValue:forKey: Bindings setup in Storyboard/XIB are always tied to NSObject subclass object (controller). Non-object values are handled specially:

    If your key-value coding compliant object receives a setValue:forKey: message with nil passed as the value for a non-object property, the default implementation has no appropriate, generalized course of action. It therefore sends itself a setNilValueForKey: message, which you can override. The default implementation of setNilValueForKey: raises an NSInvalidArgumentException exception, but you can provide an appropriate, implementation-specific behavior.

    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/HandlingNon-ObjectValues.html#//apple_ref/doc/uid/10000107i-CH5-SW1

    In your case OrderFormViewController didn't have overriden setNilValueForKey: method and it thrown exception.

    Implementing setNilValueForKey: will solve all the issues.

    PS: Using NSNumberFormatter or binding to a NSNumber object would solve the issue as well.