Search code examples
objective-cmacoscocoacocoa-bindings

Cocoa bindings not reading a nil value from user defaults


I have two UI components whose values are synchronized through user defaults. The first one is essentially an input field, the other a menu item. When the value of the input field changes, the key equivalent of the menu item changes through Cocoa bindings:

Input Field -> NSUserDefaultsController -> User Defaults
User Defaults -> NSUserDefaultsController -> Menu Item

This works perfectly for everything but a nil value. For some reason I can’t store the actual nil value in user defaults, I use an empty dictionary instead. On the other side that reads the value I have a transformer that “inflates” the empty dictionary into a nil value.

Now, when I enter a non-nil value into the input field, it gets through to user defaults and is correctly propagated all the way to the menu item. When I enter an “empty” value, an empty dictionary is correctly stored in the user defaults, but the bound value of the menu item does not change.

What am I doing wrong?


Solution

  • What I did wrong was implementing the binding on the menu item as a hack. I introduced a keyCombo property through a category:

    @interface NSMenuItem (Combo)
    @property(assign, nonatomic) MASShortcut *keyCombo;
    @end
    

    This is the property that gets bound to user defaults. Then, since I couldn’t easily add an instance variable in a category, I simply returned nil in the getter:

    @implementation NSMenuItem (Combo)
    
    - (MASShortcut*) keyCombo
    {
        return nil;
    }
    
    /* setter omitted */
    
    @end
    

    This worked well until I wanted to set the property to nil through bindings. Because the getter was always returning nil, the Cocoa bindings machinery thought there was no need to update the property value, as it seemed to be nil already.